DynamicModule[{xplayer=10,yplayer=1,playerWidth=1.5,playerHeight=0.8,invaders,(*{{x,y},alive?,type(色)}*)invDirection=1,invSpeed=0.08,invDropHeight=0.8,bullets={},(*{{x,y}}*)particles={},score=0,combo=0,comboText="",comboTimer=0,endflag=0 (*0:プレイ中,1:ゲームオーバー,2:クリア*)},(*インベーダーの初期化:4行×6列*)invadersInit[]:=Flatten[Table[{{x,y},True,Switch[y,17,Red,15,Magenta,13,Green,11,Cyan] (*敵の種類/色*)},{y,{11,13,15,17}},{x,2,17,3}],1];
invaders=invadersInit[];
ClickPane[Dynamic[(*1. マウス位置でプレイヤー（自機）を移動*)If[endflag==0,With[{mp=MousePosition["Graphics"]},If[ListQ[mp],xplayer=Clip[mp[[1]],{playerWidth,20-playerWidth}]]]];
(*2. ゲームループ（一秒間に約60回更新）*)Clock[Inf];
(*3. プレイ中のゲームロジック*)If[endflag==0,(*---インベーダーの移動ロジック---*)Module[{hitWall=False,livingInvaders},livingInvaders=Select[invaders,#[[2]]&];
(*生きている敵がいる場合のみ移動*)If[Length[livingInvaders]>0,(*左右の壁に当たったか判定*)If[Min[livingInvaders[[All,1,1]]]<1&&invDirection==-1,hitWall=True];
If[Max[livingInvaders[[All,1,1]]]>19&&invDirection==1,hitWall=True];
If[hitWall,(*壁に当たったら向きを反転して一段下がる*)invDirection=-invDirection;
Do[If[invaders[[i,2]],invaders[[i,1,2]]-=invDropHeight],{i,Length[invaders]}],(*通常は横移動*)Do[If[invaders[[i,2]],invaders[[i,1,1]]+=invDirection*invSpeed],{i,Length[invaders]}]];
(*敗北条件：敵が防衛ライン（自機の高さ）まで降りてきたらゲームオーバー*)If[Min[livingInvaders[[All,1,2]]]<=yplayer+playerHeight,endflag=1];]];
(*---弾の移動と画面外消去---*)bullets=Map[{#[[1]],#[[2]]+0.4}&,bullets];(*上方向に進む*)bullets=Select[bullets,#[[2]]<20&];(*画面外に出たら消去*)(*コンボタイマーの減少*)If[comboTimer>0,comboTimer--;If[comboTimer==0,combo=0]];
(*パーティクルの更新*)particles=DeleteCases[Map[{#[[1]]+#[[2]],#[[2]],#[[3]]-1,#[[4]]}&,particles],_?(#[[3]]<=0&)];
(*---当たり判定（弾 vs インベーダー）---*)Module[{newBullets={},bulletHit},Do[bulletHit=False;
Do[If[invaders[[j,2]],(*インベーダーが生きていたら*)Module[{ix=invaders[[j,1,1]],iy=invaders[[j,1,2]]},(*弾と敵の距離が近ければ衝突*)If[Abs[b[[1]]-ix]<1.0&&Abs[b[[2]]-iy]<0.6,invaders[[j,2]]=False;(*敵死亡*)bulletHit=True;
(*スコアとコンボの計算*)combo+=1;
score+=(150*combo);
comboText=ToString[combo]<>" CHAIN! (+"<>ToString[150*combo]<>")";
comboTimer=40;(*チェーン判定の猶予時間*)(*爆発パーティクル生成*)particles=Join[particles,Table[{{ix,iy},RandomReal[{-0.25,0.25},2],RandomInteger[{10,20}],invaders[[j,3]]},{10}]];
Break[]; (*この弾の判定は終了*)]]],{j,Length[invaders]}];
If[!bulletHit,AppendTo[newBullets,b]],(*当たっていない弾だけ残す*){b,bullets}];
bullets=newBullets;];
(*---クリア判定---*)If[And@@(!#[[2]]&/@invaders),endflag=2];];
(*4. グラフィック描画*)Graphics[{(*宇宙背景*)Black,Rectangle[{0,0},{20,20}],{Opacity[0.3],White,Point/@Table[RandomReal[{0,20},2],{30}]},(*背景の星*)(*インベーダーの描画（ドット絵風の四角形組み合わせ）*)Table[If[invaders[[i,2]],Module[{cx=invaders[[i,1,1]],cy=invaders[[i,1,2]],col=invaders[[i,3]]},{col,(*インベーダーっぽい形の簡易表現*)Rectangle[{cx-0.7,cy-0.4},{cx+0.7,cy+0.4}],Rectangle[{cx-0.4,cy+0.4},{cx+0.4,cy+0.6}],(*角/触覚*)Black,(*目*)Rectangle[{cx-0.4,cy+0.1},{cx-0.2,cy+0.3}],Rectangle[{cx+0.2,cy+0.1},{cx+0.4,cy+0.3}]}],{}],{i,Length[invaders]}],(*プレイヤーの弾*){Yellow,AbsoluteThickness[3],Line[{#-{0,0.3},#+{0,0.3}}]&/@bullets},(*パーティクル*){PointSize[0.015],Table[{#[[4]],Point[#[[1]]]},{#,particles}]&},(*プレイヤー（自機：緑色の宇宙船風）*)If[endflag==0,{Darker[Green,0.2],Rectangle[{xplayer-playerWidth,yplayer},{xplayer+playerWidth,yplayer+playerHeight}],Rectangle[{xplayer-0.3,yplayer+playerHeight},{xplayer+0.3,yplayer+playerHeight+0.4}] (*砲口*)},{}],(*UI (スコア)*)Inset[Style[Row[{"SCORE: ",score}],16,White,Bold,FontFamily->"Consolas"],{0.5,19.5},{-1,1}],(*コンボ（チェイン）表示*)If[endflag==0&&comboTimer>0&&combo>=2,Inset[Style[comboText,16,Cyan,Bold,Italic],{xplayer,yplayer+2},{0,0}],{}],(*リザルト画面*)If[endflag==1,{Opacity[0.85],Darker[Red,0.7],Rectangle[{0,0},{20,20}],Opacity[1],Inset[Style["EARTH IS DESTROYED",32,White,Bold,FontFamily->"Impact"],{10,11}],Inset[Style[Row[{"FINAL SCORE: ",score}],20,Yellow,Bold],{10,8.5}],Inset[Style["- Click to Retry -",15,GrayLevel[0.7]],{10,5}]},{}],If[endflag==2,{Opacity[0.85],Darker[Blue,0.7],Rectangle[{0,0},{20,20}],Opacity[1],Inset[Style["MISSION ACCOMPLISHED!",28,Yellow,Bold,FontFamily->"Impact"],{10,11}],Inset[Style["YOU SAVED THE SPACE!",18,White],{10,9}],Inset[Style["- Click to Play Again -",15,GrayLevel[0.7]],{10,5}]},{}]},PlotRange->{{0,20},{0,20}},ImageSize->450,AspectRatio->1]],(*操作：プレイ中にクリックされたら弾を発射、終了時はリセット*)Function[pos,If[endflag==0,(*画面内に同時に撃てる弾数を5発までに制限（連射ハメ防止）*)If[Length[bullets]<5,AppendTo[bullets,{xplayer,yplayer+playerHeight+0.4}]],(*リセット処理*)invaders=invadersInit[];
bullets={};particles={};
score=0;combo=0;comboTimer=0;
invDirection=1;endflag=0;]]]]