star_breaker_stg_stage5_power_barrage.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>STAR BREAKER - 5 STAGE STG</title>
<style>
:root{--cyan:#55efff;--pink:#ff4fd8;--yellow:#ffe66d;--bg:#02040e}
*{box-sizing:border-box}
html,body{margin:0;width:100%;height:100%;overflow:hidden;background:#02040e;color:#fff;font-family:system-ui,-apple-system,"Yu Gothic UI",sans-serif}
body{display:grid;place-items:center;background:radial-gradient(circle at 50% 20%,#17265e,#050817 50%,#010208)}
#wrap{position:relative;width:min(100vw,900px);height:min(100vh,960px);aspect-ratio:3/4;overflow:hidden;border:1px solid #55efff55;box-shadow:0 0 36px #55efff22;background:#02040e}
canvas{display:block;width:100%;height:100%;touch-action:none;cursor:crosshair}
#hud{position:absolute;inset:0;pointer-events:none;font-weight:900;text-shadow:0 0 8px #55efff}
.top{position:absolute;top:12px;left:16px;right:16px;display:flex;justify-content:space-between;gap:12px;font-size:clamp(12px,2vw,19px)}
#stageLabel{position:absolute;top:45px;left:0;right:0;text-align:center;color:#bff8ff;letter-spacing:.22em}
#bossWrap{display:none;position:absolute;top:73px;left:9%;width:82%}
#bossName{text-align:center;font-size:13px;margin-bottom:5px;color:#ffd4f5}
.bar{height:13px;border:1px solid #ffffffaa;background:#ffffff12}
#bossHp{height:100%;width:100%;background:linear-gradient(90deg,#ff4fd8,#ff9a3d)}
.overlay{position:absolute;inset:0;display:grid;place-items:center;background:linear-gradient(#02040e33,#02040ecc);z-index:5}
.panel{width:min(88%,650px);padding:clamp(22px,5vw,44px);text-align:center;background:#081027e8;border:1px solid #55efff88;box-shadow:0 0 28px #55efff33;backdrop-filter:blur(7px)}
h1{margin:0 0 10px;font-size:clamp(38px,8vw,76px);line-height:.9;color:#fff;text-shadow:0 0 9px #fff,0 0 22px #55efff,4px 4px 0 #ff4fd8}
.sub{color:#8deeff;letter-spacing:.25em;margin-bottom:22px;font-weight:800}
#message{line-height:1.8;color:#ddebff}
button{margin:20px 7px 0;padding:14px 28px;border:0;border-radius:999px;color:#fff;font-weight:900;font-size:17px;cursor:pointer;background:linear-gradient(90deg,#55efff,#7277ff,#ff4fd8);box-shadow:0 0 18px #55efff66}
button.secondary{background:#252c46;box-shadow:none;border:1px solid #ffffff44}
button:hover{filter:brightness(1.15);transform:scale(1.03)}
.small{margin-top:18px;padding-top:14px;border-top:1px solid #ffffff22;color:#b7c9dd;font-size:13px;line-height:1.7}
#mobile{position:absolute;left:0;right:0;bottom:12px;display:none;justify-content:space-between;padding:0 14px;pointer-events:none;z-index:4}
.group{display:flex;gap:8px;pointer-events:auto}.touch{width:58px;height:58px;margin:0;padding:0;border-radius:50%;opacity:.62;font-size:20px}
@media(pointer:coarse){#mobile{display:flex}}
</style>
</head>
<body>
<div id="wrap">
<canvas id="game" width="720" height="960"></canvas>

<div id="hud">
  <div class="top">
    <div>SCORE <span id="score">0000000</span></div>
    <div>HI <span id="high">0000000</span></div>
    <div>POWER <span id="power">1</span></div>
  </div>
  <div id="stageLabel">STAGE 1 / 5</div>
  <div id="bossWrap">
    <div id="bossName">BOSS</div>
    <div class="bar"><div id="bossHp"></div></div>
  </div>
</div>

<div id="overlay" class="overlay">
  <div class="panel">
    <h1 id="title">STAR<br>BREAKER</h1>
    <div class="sub" id="subtitle">5 STAGE MISSION</div>
    <div id="message">全5ステージを突破し、最終要塞を破壊せよ。<br>敵弾に一発でも当たると爆発してゲームオーバー。<br>POWERアイテムでショットを最大5段階まで強化。</div>
    <div id="buttons">
      <button id="startBtn">GAME START</button>
    </div>
    <div class="small">PC:WASD / 矢印キー / マウスドラッグで移動 | P:一時停止<br>スマホ:画面ドラッグ、または下の移動ボタン</div>
  </div>
</div>

<div id="mobile">
  <div class="group"><button class="touch" data-key="left">◀</button><button class="touch" data-key="right">▶</button></div>
  <div class="group"><button class="touch" data-key="up">▲</button><button class="touch" data-key="down">▼</button></div>
</div>
</div>

<script>
(()=>{
"use strict";
const canvas=document.getElementById("game"),ctx=canvas.getContext("2d");
const W=720,H=960;
const scoreEl=document.getElementById("score"),highEl=document.getElementById("high"),powerEl=document.getElementById("power");
const stageLabel=document.getElementById("stageLabel"),bossWrap=document.getElementById("bossWrap"),bossName=document.getElementById("bossName"),bossHp=document.getElementById("bossHp");
const overlay=document.getElementById("overlay"),title=document.getElementById("title"),subtitle=document.getElementById("subtitle"),message=document.getElementById("message"),buttons=document.getElementById("buttons");

let running=false,paused=false,dead=false,last=0,time=0,score=0,high=Number(localStorage.getItem("starBreaker5High")||0);
let stage=1,stageTime=0,waveTimer=0,wavesSpawned=0,bossActive=false,transition=false,shake=0,flash=0;
let pointer=false;
const keys={},bullets=[],enemyBullets=[],enemies=[],particles=[],powerups=[],stars=[];

const stageData=[
 {name:"ORBITAL FRONT",duration:23,boss:"IRON TALON",hp:260,bg:["#07112c","#02040e"]},
 {name:"CRIMSON NEBULA",duration:25,boss:"SCARLET MANTA",hp:380,bg:["#2b071c","#07030b"]},
 {name:"ASTEROID BELT",duration:27,boss:"GRAVITY GOLEM",hp:520,bg:["#241b10","#050403"]},
 {name:"VOID TEMPLE",duration:29,boss:"ABYSS SERAPH",hp:700,bg:["#16072a","#030109"]},
 {name:"FINAL FORTRESS",duration:32,boss:"OMEGA EMPRESS",hp:980,bg:["#051b26","#010408"]}
];

const player={x:W/2,y:H-120,r:11,speed:410,cool:0,rate:.12,power:1,maxPower:5,inv:0,tilt:0};
for(let i=0;i<150;i++)stars.push({x:Math.random()*W,y:Math.random()*H,z:Math.random()*2.5+.5,a:Math.random()*.8+.2});

function clearAll(){bullets.length=enemyBullets.length=enemies.length=particles.length=powerups.length=0}
function updateHUD(){
 scoreEl.textContent=String(Math.floor(score)).padStart(7,"0");
 highEl.textContent=String(Math.floor(high)).padStart(7,"0");
 powerEl.textContent=player.power>=player.maxPower?"MAX":player.power;
 stageLabel.textContent=`STAGE ${stage} / 5 ${stageData[stage-1].name}`;
}
function reset(){
 clearAll();stage=1;stageTime=0;waveTimer=0;wavesSpawned=0;bossActive=false;transition=false;dead=false;paused=false;
 score=0;time=0;shake=0;flash=0;player.x=W/2;player.y=H-120;player.cool=0;player.power=1;player.inv=1;
 bossWrap.style.display="none";updateHUD();
}
function begin(){
 reset();running=true;overlay.style.display="none";last=performance.now();requestAnimationFrame(loop);
}
function addScore(n){
 score+=n;if(score>high){high=score;localStorage.setItem("starBreaker5High",String(Math.floor(high)))}updateHUD();
}
function burst(x,y,color,count=25,speed=260){
 for(let i=0;i<count;i++){const a=Math.random()*Math.PI*2,s=Math.random()*speed+40;
 particles.push({x,y,vx:Math.cos(a)*s,vy:Math.sin(a)*s,life:Math.random()*.7+.3,max:1,size:Math.random()*6+2,color});
 }}
function shoot(){
 let shots=[];
 if(player.power===1)shots=[{a:0,x:0,d:1}];
 if(player.power===2)shots=[{a:-.10,x:-7,d:1},{a:.10,x:7,d:1}];
 if(player.power===3)shots=[{a:-.18,x:-10,d:1},{a:0,x:0,d:2},{a:.18,x:10,d:1}];
 if(player.power===4)shots=[{a:-.25,x:-15,d:2},{a:-.08,x:-5,d:2},{a:.08,x:5,d:2},{a:.25,x:15,d:2}];
 if(player.power>=5)shots=[
   {a:-.31,x:-18,d:2},{a:-.16,x:-10,d:2},{a:0,x:0,d:3},
   {a:.16,x:10,d:2},{a:.31,x:18,d:2}
 ];
 shots.forEach(s=>bullets.push({
   x:player.x+s.x,y:player.y-20,
   vx:Math.sin(s.a)*230,vy:-820,
   r:s.d>=3?7:5,dmg:s.d,power:player.power
 }));
 player.rate=Math.max(.065,.13-player.power*.013);
}
function spawnEnemy(type,x,y){
 const s=stage;
 if(type==="scout")enemies.push({type,x,y,r:19,hp:1+s,maxHp:1+s,vx:(Math.random()-.5)*130,vy:105+s*11,age:0,shot:1.2+Math.random()});
 if(type==="zig")enemies.push({type,x,y,r:22,hp:3+s,maxHp:3+s,vx:0,vy:80+s*7,age:Math.random()*5,shot:.9+Math.random()});
 if(type==="tank")enemies.push({type,x,y,r:32,hp:11+s*4,maxHp:11+s*4,vx:0,vy:42+s*3,age:0,shot:.7});
}
function spawnWave(){
 wavesSpawned++;
 const pattern=(wavesSpawned+stage)%4;
 if(pattern===0){for(let i=0;i<7;i++)spawnEnemy("scout",75+i*95,-40-i*34)}
 if(pattern===1){for(let i=0;i<6;i++)spawnEnemy("zig",90+i*108,-45-i*70)}
 if(pattern===2){spawnEnemy("tank",W/2,-70);for(let i=0;i<4;i++)spawnEnemy("scout",120+i*160,-160-i*40)}
 if(pattern===3){for(let i=0;i<3;i++){spawnEnemy("zig",170+i*190,-60-i*80);spawnEnemy("scout",90+i*250,-190-i*50)}}
}
function spawnBoss(){
 bossActive=true;enemyBullets.length=0;
 const d=stageData[stage-1];
 enemies.push({type:"boss",x:W/2,y:-130,r:82,hp:d.hp,maxHp:d.hp,age:0,shot:.6,stage,vy:48});
 bossName.textContent=d.boss;bossHp.style.width="100%";bossWrap.style.display="block";
}
function addEnemyBullet(x,y,a,speed,r=7,color="#ff8ae7"){
 enemyBullets.push({x,y,vx:Math.cos(a)*speed,vy:Math.sin(a)*speed,r,color});
}
function enemyShoot(e){
 const aim=Math.atan2(player.y-e.y,player.x-e.x);
 const base=145+stage*18;

 if(e.type==="scout"){
   if(stage<=2){
     for(let i=-1;i<=1;i++)addEnemyBullet(e.x,e.y,aim+i*.12,base,6,"#ff8ae7");
   }else{
     for(let i=-2;i<=2;i++)addEnemyBullet(e.x,e.y,aim+i*.13,base+12,6,"#ff8ae7");
   }
 }

 if(e.type==="zig"){
   const count=5+stage;
   for(let i=0;i<count;i++){
     const a=(i/(count-1)-.5)*1.15+Math.PI/2;
     addEnemyBullet(e.x,e.y,a,145+stage*12,6,"#9d8cff");
   }
   addEnemyBullet(e.x,e.y,aim,210+stage*8,5,"#ffffff");
 }

 if(e.type==="tank"){
   const n=10+stage*2;
   for(let i=0;i<n;i++){
     const a=i*Math.PI*2/n+e.age*.32;
     addEnemyBullet(e.x,e.y,a,125+stage*10,7,"#ffb25f");
   }
   for(let i=-2;i<=2;i++)addEnemyBullet(e.x,e.y,aim+i*.15,190+stage*10,6,"#fff0a8");
 }

 if(e.type==="boss"){
   const ratio=e.hp/e.maxHp;

   if(e.stage===1){
     for(let ring=0;ring<2;ring++){
       for(let i=-5;i<=5;i++){
         const a=aim+i*.12+(ring?0.055:0);
         addEnemyBullet(e.x,e.y+35,a,195+ring*45,7,ring?"#ffd1f4":"#ff78d8");
       }
     }
   }

   if(e.stage===2){
     const n=22;
     for(let i=0;i<n;i++){
       const a=i*Math.PI*2/n+e.age*.62;
       addEnemyBullet(e.x,e.y,a,155+(i%2)*42,7,i%2?"#ff7ac8":"#a884ff");
     }
     for(let i=-2;i<=2;i++)addEnemyBullet(e.x,e.y+25,aim+i*.13,235,6,"#ffffff");
   }

   if(e.stage===3){
     for(let ring=0;ring<3;ring++){
       const n=12+ring*4;
       for(let i=0;i<n;i++){
         const a=i*Math.PI*2/n+e.age*(ring%2?-.42:.42);
         addEnemyBullet(e.x,e.y,a,115+ring*42,8-ring,"#ffb35f");
       }
     }
   }

   if(e.stage===4){
     for(let side=-1;side<=1;side+=2){
       for(let i=0;i<9;i++){
         const a=aim+side*(.08+i*.085)+Math.sin(e.age*1.8)*.18;
         addEnemyBullet(e.x+side*45,e.y+20,a,205+i*6,6,"#b477ff");
       }
     }
     if(ratio<.5){
       for(let i=0;i<18;i++){
         const a=i*Math.PI/9-e.age*.55;
         addEnemyBullet(e.x,e.y,a,175,7,"#ff70dd");
       }
     }
   }

   if(e.stage===5){
     const n=ratio>.6?28:38;
     for(let i=0;i<n;i++){
       const a=i*Math.PI*2/n+e.age*.78;
       addEnemyBullet(e.x,e.y,a,150+(i%4)*28,7,i%2?"#ff4fd8":"#65eaff");
     }
     for(let i=-5;i<=5;i++)addEnemyBullet(e.x,e.y+42,aim+i*.095,250,6,"#fff2a8");
     if(ratio<.45){
       for(let i=0;i<20;i++){
         const a=i*Math.PI/10-e.age*1.05;
         addEnemyBullet(e.x,e.y,a,225,5,"#ffffff");
       }
     }
   }
 }
}
function collide(a,b){const dx=a.x-b.x,dy=a.y-b.y;return dx*dx+dy*dy<(a.r+b.r)*(a.r+b.r)}
function gameOver(){
 if(dead)return;dead=true;running=false;shake=34;flash=.75;
 burst(player.x,player.y,"#ff4fd8",120,430);burst(player.x,player.y,"#ffe66d",80,360);
 draw();
 setTimeout(()=>{
  title.innerHTML="GAME<br>OVER";subtitle.textContent="SHIP DESTROYED";
  message.innerHTML=`敵弾に命中し、機体が爆発した。<br>SCORE:<b>${Math.floor(score).toLocaleString()}</b> STAGE:<b>${stage}</b>`;
  buttons.innerHTML='<button id="retryBtn">リトライ</button><button id="quitBtn" class="secondary">やめる</button>';
  overlay.style.display="grid";
  document.getElementById("retryBtn").addEventListener("click",begin);
  document.getElementById("quitBtn").addEventListener("click",quitGame);
 },650);
}
function quitGame(){
 clearAll();dead=false;bossWrap.style.display="none";
 title.innerHTML="STAR<br>BREAKER";subtitle.textContent="MISSION ABORTED";
 message.innerHTML="ゲームを終了しました。<br>もう一度遊ぶときはスタートを押してください。";
 buttons.innerHTML='<button id="againBtn">GAME START</button>';
 document.getElementById("againBtn").addEventListener("click",begin);
 draw();
}
function stageClear(){
 transition=true;bossActive=false;bossWrap.style.display="none";enemyBullets.length=0;clearAll();
 addScore(2500*stage);
 if(stage===5){running=false;title.innerHTML="ALL<br>CLEAR";subtitle.textContent="OMEGA FORTRESS DESTROYED";
  message.innerHTML=`全5ステージ制覇!<br>FINAL SCORE:<b>${Math.floor(score).toLocaleString()}</b>`;
  buttons.innerHTML='<button id="retryAll">もう一度遊ぶ</button><button id="quitAll" class="secondary">やめる</button>';
  overlay.style.display="grid";
  document.getElementById("retryAll").addEventListener("click",begin);
  document.getElementById("quitAll").addEventListener("click",quitGame);
  return;
 }
 running=false;title.innerHTML=`STAGE ${stage}<br>CLEAR`;subtitle.textContent=stageData[stage-1].boss+" DESTROYED";
 message.innerHTML=`次は STAGE ${stage+1}「${stageData[stage].name}」`;
 buttons.innerHTML='<button id="nextStage">NEXT STAGE</button>';
 overlay.style.display="grid";
 document.getElementById("nextStage").addEventListener("click",()=>{
   stage++;stageTime=0;waveTimer=0;wavesSpawned=0;transition=false;player.x=W/2;player.y=H-120;player.inv=1.5;
   updateHUD();overlay.style.display="none";running=true;last=performance.now();requestAnimationFrame(loop);
 });
}
function update(dt){
 time+=dt;stageTime+=dt;if(player.inv>0)player.inv-=dt;if(shake>0)shake=Math.max(0,shake-dt*55);if(flash>0)flash=Math.max(0,flash-dt*1.7);
 stars.forEach(s=>{s.y+=(55+s.z*65+stage*8)*dt;if(s.y>H){s.y=-5;s.x=Math.random()*W}});
 let dx=0,dy=0;if(keys.ArrowLeft||keys.a||keys.left)dx--;if(keys.ArrowRight||keys.d||keys.right)dx++;if(keys.ArrowUp||keys.w||keys.up)dy--;if(keys.ArrowDown||keys.s||keys.down)dy++;
 const l=Math.hypot(dx,dy)||1;dx/=l;dy/=l;player.x+=dx*player.speed*dt;player.y+=dy*player.speed*dt;player.x=Math.max(25,Math.min(W-25,player.x));player.y=Math.max(95,Math.min(H-35,player.y));player.tilt+=(dx*.45-player.tilt)*Math.min(1,dt*10);
 player.cool-=dt;if(player.cool<=0){shoot();player.cool=player.rate}
 if(!bossActive){
   waveTimer-=dt;if(waveTimer<=0){spawnWave();waveTimer=Math.max(2.5,4.6-stage*.28)}
   if(stageTime>=stageData[stage-1].duration)spawnBoss();
 }
 for(let i=bullets.length-1;i>=0;i--){const b=bullets[i];b.x+=b.vx*dt;b.y+=b.vy*dt;if(b.y<-30||b.x<-30||b.x>W+30)bullets.splice(i,1)}
 for(let i=enemyBullets.length-1;i>=0;i--){const b=enemyBullets[i];b.x+=b.vx*dt;b.y+=b.vy*dt;if(b.y>H+50||b.y<-60||b.x<-60||b.x>W+60){enemyBullets.splice(i,1);continue}if(player.inv<=0&&collide(b,player)){enemyBullets.splice(i,1);gameOver();return}}
 for(let i=enemies.length-1;i>=0;i--){const e=enemies[i];e.age+=dt;
   if(e.type==="scout"){e.x+=e.vx*dt;e.y+=e.vy*dt;if(e.x<30||e.x>W-30)e.vx*=-1}
   if(e.type==="zig"){e.y+=e.vy*dt;e.x+=Math.sin(e.age*2.5)*125*dt}
   if(e.type==="tank"){e.y+=e.vy*dt;e.x=W/2+Math.sin(e.age*.85)*220}
   if(e.type==="boss"){if(e.y<165)e.y+=e.vy*dt;else e.x=W/2+Math.sin(e.age*(.55+e.stage*.06))*245;bossHp.style.width=Math.max(0,e.hp/e.maxHp*100)+"%"}
   e.shot-=dt;if(e.shot<=0&&e.y>0){enemyShoot(e);e.shot=e.type==="boss"?Math.max(.22,.62-stage*.065):e.type==="tank"?1.08:e.type==="zig"?.92:1.12}
   for(let j=bullets.length-1;j>=0;j--){const b=bullets[j];if(collide(b,e)){e.hp-=b.dmg;bullets.splice(j,1);burst(b.x,b.y,"#ffe66d",4,80);
     if(e.hp<=0){const boss=e.type==="boss";burst(e.x,e.y,boss?"#ff4fd8":"#55efff",boss?130:28,boss?430:230);shake=boss?30:7;
       addScore(boss?5000*stage:e.type==="tank"?550:e.type==="zig"?220:100);
       if(!boss){
         const dropRate=e.type==="tank"?.60:e.type==="zig"?.32:.20;
         if(Math.random()<dropRate)powerups.push({x:e.x,y:e.y,r:15,vy:82,spin:0});
       }
       enemies.splice(i,1);if(boss){enemyBullets.length=0;setTimeout(stageClear,550)}break;
     }}}
   if(i<enemies.length&&e.y>H+100)enemies.splice(i,1);
   else if(i<enemies.length&&player.inv<=0&&collide(e,player)){gameOver();return}
 }
 for(let i=powerups.length-1;i>=0;i--){const p=powerups[i];p.y+=p.vy*dt;p.spin+=dt*4;if(collide(p,player)){player.power=Math.min(player.maxPower,player.power+1);addScore(player.power>=player.maxPower?800:400);burst(p.x,p.y,"#ffe66d",20,190);powerups.splice(i,1);updateHUD()}else if(p.y>H+30)powerups.splice(i,1)}
 for(let i=particles.length-1;i>=0;i--){const p=particles[i];p.x+=p.vx*dt;p.y+=p.vy*dt;p.vx*=.984;p.vy*=.984;p.life-=dt;if(p.life<=0)particles.splice(i,1)}
}
function drawShip(){
 ctx.save();ctx.translate(player.x,player.y);ctx.rotate(player.tilt);if(player.inv>0&&Math.floor(player.inv*13)%2===0)ctx.globalAlpha=.3;
 ctx.shadowBlur=22;ctx.shadowColor="#55efff";ctx.fillStyle="#e9fdff";ctx.beginPath();ctx.moveTo(0,-27);ctx.lineTo(18,19);ctx.lineTo(0,10);ctx.lineTo(-18,19);ctx.closePath();ctx.fill();
 ctx.fillStyle="#ff4fd8";ctx.beginPath();ctx.moveTo(0,-13);ctx.lineTo(7,11);ctx.lineTo(-7,11);ctx.closePath();ctx.fill();
 ctx.shadowColor="#ff9a3d";ctx.fillStyle="#fff";ctx.beginPath();ctx.moveTo(-7,15);ctx.lineTo(0,35+Math.random()*9);ctx.lineTo(7,15);ctx.closePath();ctx.fill();ctx.restore();
}
function drawEnemy(e){
 ctx.save();ctx.translate(e.x,e.y);
 if(e.type==="boss"){ctx.rotate(Math.sin(e.age*.8)*.07);ctx.shadowBlur=28;ctx.shadowColor="#ff4fd8";ctx.fillStyle=`hsl(${305-e.stage*18} 65% 22%)`;ctx.beginPath();ctx.moveTo(0,-72);ctx.lineTo(96,-18);ctx.lineTo(65,60);ctx.lineTo(0,36);ctx.lineTo(-65,60);ctx.lineTo(-96,-18);ctx.closePath();ctx.fill();ctx.strokeStyle="#ff76df";ctx.lineWidth=5;ctx.stroke();ctx.fillStyle="#fff";ctx.beginPath();ctx.arc(0,-4,25,0,Math.PI*2);ctx.fill();ctx.fillStyle="#ff4fd8";ctx.beginPath();ctx.arc(0,-4,11,0,Math.PI*2);ctx.fill()}
 else{const c=e.type==="tank"?"#ff9a3d":e.type==="zig"?"#a873ff":"#55efff";ctx.shadowBlur=16;ctx.shadowColor=c;ctx.fillStyle=c;if(e.type==="tank"){ctx.beginPath();ctx.roundRect(-29,-24,58,48,12);ctx.fill();ctx.fillStyle="#fff";ctx.fillRect(-8,-31,16,24)}else{ctx.rotate(Math.PI);ctx.beginPath();ctx.moveTo(0,-24);ctx.lineTo(22,18);ctx.lineTo(0,8);ctx.lineTo(-22,18);ctx.closePath();ctx.fill();ctx.fillStyle="#fff";ctx.beginPath();ctx.arc(0,2,6,0,Math.PI*2);ctx.fill()}}
 ctx.restore();
}
function draw(){
 const bg=stageData[Math.max(0,stage-1)].bg,g=ctx.createLinearGradient(0,0,0,H);g.addColorStop(0,bg[0]);g.addColorStop(1,bg[1]);ctx.fillStyle=g;ctx.fillRect(0,0,W,H);
 ctx.save();if(shake>0)ctx.translate((Math.random()-.5)*shake,(Math.random()-.5)*shake);
 stars.forEach(s=>{ctx.globalAlpha=s.a;ctx.fillStyle="#fff";ctx.fillRect(s.x,s.y,s.z,s.z*3)});ctx.globalAlpha=1;
 for(const b of bullets){ctx.shadowBlur=15;ctx.shadowColor="#55efff";ctx.fillStyle="#efffff";ctx.fillRect(b.x-3,b.y-13,6,24)}ctx.shadowBlur=0;
 for(const b of enemyBullets){ctx.shadowBlur=14;ctx.shadowColor=b.color||"#ff4fd8";ctx.fillStyle=b.color||"#ff8ae7";ctx.beginPath();ctx.arc(b.x,b.y,b.r,0,Math.PI*2);ctx.fill()}ctx.shadowBlur=0;
 enemies.forEach(drawEnemy);
 powerups.forEach(p=>{ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.spin);ctx.shadowBlur=18;ctx.shadowColor="#ffe66d";ctx.fillStyle="#ffe66d";ctx.beginPath();for(let i=0;i<8;i++){const a=i*Math.PI/4,r=i%2?7:15;ctx.lineTo(Math.cos(a)*r,Math.sin(a)*r)}ctx.closePath();ctx.fill();ctx.restore()});
 particles.forEach(p=>{ctx.globalAlpha=Math.max(0,p.life/p.max);ctx.fillStyle=p.color;ctx.fillRect(p.x,p.y,p.size,p.size)});ctx.globalAlpha=1;
 if(!dead)drawShip();
 if(paused){ctx.fillStyle="#0009";ctx.fillRect(0,0,W,H);ctx.fillStyle="#fff";ctx.textAlign="center";ctx.font="bold 54px system-ui";ctx.fillText("PAUSE",W/2,H/2)}
 if(flash>0){ctx.globalAlpha=flash;ctx.fillStyle="#fff";ctx.fillRect(0,0,W,H);ctx.globalAlpha=1}
 ctx.restore();
}
function loop(now){if(!running)return;const dt=Math.min(.033,(now-last)/1000);last=now;if(!paused)update(dt);draw();if(running)requestAnimationFrame(loop)}
function setPointer(e){const r=canvas.getBoundingClientRect();player.x=(e.clientX-r.left)/r.width*W;player.y=(e.clientY-r.top)/r.height*H}
addEventListener("keydown",e=>{keys[e.key]=true;keys[e.key.toLowerCase()]=true;if(e.key.toLowerCase()==="p"&&running)paused=!paused;if(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"," "].includes(e.key))e.preventDefault()});
addEventListener("keyup",e=>{keys[e.key]=false;keys[e.key.toLowerCase()]=false});
canvas.addEventListener("pointerdown",e=>{pointer=true;setPointer(e)});canvas.addEventListener("pointermove",e=>{if(pointer)setPointer(e)});addEventListener("pointerup",()=>pointer=false);
document.querySelectorAll(".touch").forEach(b=>{const k=b.dataset.key;b.addEventListener("pointerdown",e=>{e.preventDefault();keys[k]=true});b.addEventListener("pointerup",()=>keys[k]=false);b.addEventListener("pointercancel",()=>keys[k]=false)});
document.getElementById("startBtn").addEventListener("click",begin);
highEl.textContent=String(high).padStart(7,"0");updateHUD();draw();
})();
</script>
</body>
</html>

投稿者: chosuke

趣味はゲームやアニメや漫画などです

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です