2018年 5月5日
今回はショットを複数撃てるようにした。
それかから switch文を使ってタイトル、ゲーム、ゲームオーバーのモードを分けるようにした。
それではソースを
; 企画第一弾 : 1画面防衛シューティングを作ろう ; var.0.002 #include "hsp3dish.as" ; HSPdishの宣言 randomize ; 乱数の初期化 wx=640 ; 画面の大きさx wy=360 ; 画面の大きさy ox=20 ; キャラ大きさx oy=20 ; キャラ大きさy font "",ox ; キャラの大きさ設定 ; 自機 ---------- px=wx/2-ox/2 ; プレイヤーの座標x py=wy/2-oy/2 ; プレイヤーの座標y ; SHOT ---------- sb=20 ; ショット最大数 dim sf,sb ; ショット発射フラグ ddim sx,sb ; ショット座標x ddim sy,sb ; ショット座標y ddim sdx,sb ; ショット移動量x ddim sdy,sb ; ショット移動量y ssp=12.0 ; スピード sc=0 ; 出現カウント scb=6 ; 発射間隔(フレーム) ; ENEMY ---------- eb=40 ; 敵最大数 dim ef,eb ; 出現フラグ ddim ex,eb ; 敵座標x ddim ey,eb ; 敵座標y ddim edx,eb ; 敵移動量x ddim edy,eb ; 敵移動量y esp=3.0 ; 敵スピード ec=0 ; 出現カウント ecb=7 ; 出現間隔(フレーム) scr=0 ; スコア hiscr=0 ; ハイスコア *main mainf=0 ; ゲームフラグ repeat ; メインループ ---------------------------------- switch mainf case 0 ; タイトル ---------- stick k : if k=0 : swbreak ; ボタンでゲーム再開 mainf=1 swbreak ; ---------- case 1 ; ゲーム ---------- ; 自機 ---------- x=mousex : y=mousey ; マウスの座標 ; SHOT ---------- repeat 1 ; 発射 if sc>0 : sc- : break ; ウエイト中 repeat sb if sf(cnt)>0 : continue ; 使用中 sf(cnt)=1 ; 使用フラグon sx(cnt)=double(px) : sy(cnt)=double(py) ; 初期座標 xn=double(x-px) ; 自機とマウスの距離x yn=double(y-py) ; 自機とマウスの距離y rad=atan(xn,yn) ; 距離xyから角度を求める sdx(cnt)=sin(rad)*ssp ; 角度とスピードから移動量xを求める sdy(cnt)=cos(rad)*ssp ; 角度とスピードから移動量yを求める break loop sc=scb ; 発射ウエイト loop repeat sb : scn=cnt ; ショット移動処理 if sf(scn)=0 : continue ; 無し sx(scn)+=sdx(scn) ; xの移動 sy(scn)+=sdy(scn) ; yの移動 repeat eb ; 敵との当たり判定 if ef(cnt)=0 : continue if sx(scn)<ex(cnt)+ox and ex(cnt)<sx(scn)+ox and sy(scn)<ey(cnt)+oy and ey(cnt)<sy(scn)+oy { ef(cnt)=0 : sf(scn)=0 ; 当たった敵とショットを消す scr+ ; スコア if hiscr<scr : hiscr=scr ; ハイスコアの更新 break } loop if sf(scn)=1 : if sx(scn)<0-ox or wx<sx(scn) or sy(scn)<0-oy or wy<sy(scn) : sf(scn)=0 ; 場外でフラグ off loop ; ENEMY ---------- repeat 1 ; 敵出現 if ec>0 : ec- : break ; ウエイト中 repeat eb ; フラグ確認 if ef(cnt)>0 : continue ; 使用中 ef(cnt)=1 ; 使用フラグon r=rnd(4) ; 出現方向 if r=0 : exs=rnd(wx) : eys=-oy ; 上 if r=1 : exs=rnd(wx) : eys=wy ; 下 if r=2 : exs=-ox : eys=rnd(wy) ; 左 if r=3 : exs=wx : eys=rnd(wy) ; 右 ex(cnt)=double(exs) ; 初期位置x ey(cnt)=double(eys) ; 初期位置y xn=double(px-exs) ; 敵と自機の距離x yn=double(py-eys) ; 敵と自機の距離y rad=atan(xn,yn) ; 距離xyから角度を求める edx(cnt)=sin(rad)*esp ; 角度とスピードから移動量xを求める edy(cnt)=cos(rad)*esp ; 角度とスピードから移動量yを求める ec=ecb ; 出現ウエイト break ; 登録終わり loop loop repeat eb ; 敵移動処理 if ef(cnt)=0 : continue ; 無し ex(cnt)+=edx(cnt) : ey(cnt)+=edy(cnt) ; 移動 if px<ex(cnt)+ox and ex(cnt)<px+ox and py<ey(cnt)+oy and ey(cnt)<py+oy { mainf=2 : break ; 自機と当たりで GAME OVER } if ex(cnt)<0-ox or wx<ex(cnt) or ey(cnt)<0-oy or wy<ey(cnt) : ef(cnt)=0 ; 枠外でフラグ off loop swbreak ; ---------- case 2 ; gameover ---------- stick k : if k=0 : swbreak ; ボタンでゲーム再開 repeat eb ef(cnt)=0 ; 敵リセット loop repeat sb sf(cnt)=0 ; ショットリセット loop scr=0 ; スコアリセット mainf=0 ; ゲーム再開 swbreak ; ---------- swend redraw 0 ; 表示 ==================== color 1,1,1 : boxf : color 255,255,255 ; 画面クリア pos px,py : mes "●" ; 自機 repeat sb ; ショット if sf(cnt)=0 : continue ; 無い pos sx(cnt),sy(cnt) : mes "・" ; ショット loop repeat eb ; 敵 if ef(cnt)=0 : continue ; 無い pos ex(cnt),ey(cnt) : mes "敵" ; 敵 loop switch mainf case 0 ; タイトル pos wx/2-120,wy/2-50 : mes "1画面防衛シューティング" pos wx/2-43,wy/2+30 : mes "var.0.002" swbreak case 1 ; ゲーム swbreak case 2 ; gameover pos wx/2-43,wy/2+50 : mes "GAME OVER" swbreak swend pos wx-300,5 : mes "SCORE "+scr pos wx-150,5 : mes "HI SCORE "+hiscr redraw 1 ; ==================== await 1000/15 ; ウエイト、FPS 15 loop ; -------------------------------------------------- end ; 終わり
それではプログラムの説明
今回は単発だったショットを複数にし、敵と同じで一定の間隔で発射するようにした。
見てもらえれば分けるけど、プログラム的に敵のソースとほとんど同じ。
sf,sx,sdx,sy,sdyを配列変数にしただけだ。
ちょっと説明が要るのは、ショットと敵の当たり判定は 1つの弾と敵全部を総当りする必要がある。
これもなんか「外積」とか言うのを使うと当たり判定を高速にできるらしいんだけど、外積は大学の数学らしくて自分はやり方を知らない。
ショットも敵も敵弾(まだ無いが)も大体同じ処理をする。
1、出現処理
a、フラグon
b、初期位置をセット
c、移動値をセット
2、移動処理
a、現座標に移動量を +
b、当たり判定
c、場外判定
出現処理
repeat sb if sf(cnt)>0 : continue sf(cnt)=1 sx(cnt)=double(px) : sy(cnt)=double(py) xn=double(x-px) sdx(cnt)=sin(rad)*ssp break sc=scb |
移動処理
scn=cnt sx(scn)+=sdx(scn) repeat eb if sx(scn)<ex(cnt)+ox and ex(cnt)<sx(scn)+ox and sy(scn)<ey(cnt)+oy and ey(cnt)<sy(scn)+oy { ef(cnt)=0 : sf(scn)=0 scr+ break if sf(scn)=1 : if sx(scn)<0-ox or wx<sx(scn) or sy(scn)<0-oy or wy<sy(scn) : sf(scn)=0 |
さて、この仕様だとショットと敵の発生間隔が同じだった場合、百発百中だと延々とゲームが続く。
敵を撃ちもらした分だけ包囲網が狭まるのだが、そこでショットの発生を敵より僅かに早くすると追い詰められてからの復帰ができる。
この場合、敵のスピードが遅かったり、発生間隔が短いと簡単すぎてやはりゲームが終わらない。
そこで敵のスピードを早くして、ミスを誘発するようにした。
ゲームバランスを考えるのに一番時間がかかってる。
mainfを switch文にかけて、タイトル画面とゲームと GAMEOVER画面を振り分けてる。
つまり mainf=0はタイトル、1=ゲームモード、2=GAMEOVER画面モードだ。
表示も同じやり方で、前よりスッキリしたと思う。