2018年 5月25日
今回はショットをマシンガンみたいにしてみる。
変更点としては、
1、ショットの速度を出すためにショットの移動処理を 1フレームに複数回する
2、照準か敵ヒット場所に着弾する
3、敵に耐久度をもうけ一撃で倒されないようにする
ver.0.004
1、ショットの速度を出すために移動処理を 1フレームに複数回する
これはなぜかと言うと、敵の大きさ以上の速さでショットを移動させると「すり抜ける」からです。
そうなると、例えば敵の大きさが 20ドットだったとして、1フレームにショットを 20ドット以上の速さで動かせません。
でもマシンガンなら弾速は早くないとね。
これも本当は面と線との当たり判定とかすれば 1回で済むと思うんですけど、数学的に少し難しいと思うので今回はしません。
そこで今回はもっとも初歩的な方法で、1フレームに複数回ショットの移動処理と敵との当たり判定の処理をする方法を使います。
1フレームに 3回移動させて、表示は 3回目の座標の 1回となり、移動スピードは 1フレームで20 × 3 = 60となります。
3回移動させる必要があるのは、敵との当たり判定を都度するためです。
ちょっと間の弾の表示が抜けるので間延びした感じにはなりますが、だから本来 STGは FPS60(1秒間に 60回表示)で作る事が多くて、このゲームでは FPS15ですけど、FPS60なら何もしなくても 1フレーム 20移動 × 4倍FPSで 4倍の速度が出て、表示も 4倍してくれる訳です。
しかしこれはブラウザゲーム前提なので、様々な環境を考慮してあえて FPS15にしてます。
もう一つの方法は、最初に 3発発射させて 1つ目は2回、2つ目は 1回、3つ目は 0回移動させておく、という方法を考えましたけど、ちょっとややこしくなるので、とにかく今回はもっともベタな方法にしてます。
照準か敵ヒット場所に着弾する
前回までは画面端まで飛んで行きましたが、今回は照準地点で止まるようにしてます。
合わせてマシンガンらしく、集弾率表現として着弾点がランダムにずれるようにしてます。
それからグラを用意して着弾表示をするようにしました、マシンガンらしいでしょう。
← 小さいけど着弾エフェクト
着弾点のずらしは、初歩的に最初のカーソル座標に乱数で + -してずらしてますが、本当はこれだと遠くを狙えば狙うほど集弾率が良くなってしまうので、ラジアン(円周率)の値にランダムで + -した方が均等で良いと思いますが今回は割愛します。
着弾判定ですが、始めに全移動距離を測定しておき、移動した量がそれを上回れば着弾地点に座標を固定という方法をとってます。
敵に耐久度をもうける
装甲がゴリゴリと削れる表現として、敵に HP(耐久度)をもうけました。
集弾率表現と合わせて、ずいぶんマシンガンらしくなったのではないでしょうか。
本当は後、効果音を付ければだいぶらしくなると思いますが、まだ割愛します。
それではソースを
; 企画第一弾 : 1画面防衛シューティングを作ろう ; var.0.004 #include "hsp3dish.as" ; HSPdishの宣言 randomize ; 乱数の初期化 pai=3.141593 pai2=pai*2 rad45=0.7853982 ; ラジアンで45度 rad90=1.5707965 ; ラジアンで90度 wx=640 ; 画面の大きさx wy=360 ; 画面の大きさy gmode 2 jw=1 : jo=20 : jo2=jo/2 ; 自機 celload "jiki_1.png",jw celdiv jw,jo,jo,jo2,jo2 sw=2 : so=6 : so2=so/2 ; ショット celload "shot_1.png",sw celdiv sw,so,so,so2,so2 ew=3 : eo=20 : eo2=eo/2 ; 敵 celload "ene_1.png",ew celdiv ew,eo,eo,eo2,eo2 cw=4 : co=32 : co2=co/2 ; カーソル celload "csr_2.png",cw celdiv cw,co,co,co2,co2 hw=5 : ho=16 : ho2=ho/2 ; ヒット celload "hit_4.png",hw celdiv hw,ho,ho,ho2,ho2 ; 自機 ---------- jx=wx/2 ; プレイヤーの座標x jy=wy/2 ; プレイヤーの座標y jx1=jx-jo2 ; 自機左座標 jx2=jx+jo2 ; 自機右座標 jy1=jy-jo2 ; 自機上座標 jy2=jy+jo2 ; 自機下座標 ; SHOT ---------- sb=20 ; ショット最大数 dim sf,sb ; ショット発射フラグ ddim sx,sb ; ショット座標x ddim sy,sb ; ショット座標y ddim sdx,sb ; ショット移動量x ddim sdy,sb ; ショット移動量y ddim sr,sb ; 向き dim sxb,sb ; 終点座標x dim syb,sb ; 終点座標y ddim skn,sb ; 現移動距離 ddim skb,sb ; 最終移動距離 ssp=20.0 ; スピード skc=3 ; 1フレームで移動計算する回数 sc=0 ; 出現カウント scb=0 ; 発射間隔(フレーム) su=32 ; 揺れ幅(集弾値) ; ENEMY ---------- eb=40 ; 敵最大数 dim ef,eb ; 出現フラグ dim ehp,eb ; 耐久度 ddim ex,eb ; 敵座標x ddim ey,eb ; 敵座標y ddim edx,eb ; 敵移動量x ddim edy,eb ; 敵移動量y ddim er,eb ; 向き ehpb=40 ; 耐久度 esp=1.0;3 ; 敵スピード ec=0 ; 出現カウント ecb=60;4 ; 出現間隔(フレーム) 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(jx) : sy(cnt)=double(jy) ; 初期座標 xx=x-su/2+rnd(su) ; ショットの着弾点x yy=y-su/2+rnd(su) ; ショットの着弾点y xn=double(xx-jx) ; 自機とマウスの距離x yn=double(yy-jy) ; 自機とマウスの距離y rad=atan(xn,yn) ; 距離xyから角度を求める sr(cnt)=-rad+pai ; 向き sdx(cnt)=sin(rad)*ssp ; 角度とスピードから移動量xを求める sdy(cnt)=cos(rad)*ssp ; 角度とスピードから移動量yを求める sxb(cnt)=xx : syb(cnt)=yy ; 終点座標 skn(cnt)=0.0 ; 現移動距離をリセット x2=xx-jx : y2=yy-jy skb(cnt)=sqrt(x2*x2+y2*y2); 最終移動距離 break loop sc=scb ; 発射ウエイト loop repeat sb : scn=cnt ; ショット移動処理 if sf(scn)=0 : continue ; 無し repeat skc : cnt2=cnt ; 1ショットの移動繰り返し ==================== sx(scn)+=sdx(scn) ; xの移動 sy(scn)+=sdy(scn) ; yの移動 skn(scn)+=ssp ; どれだけ距離を進んだか if skn(scn)>=skb(scn) { ; 終点判定 sx(scn)=double(sxb(scn)) sy(scn)=double(syb(scn)) sf(scn)=-1 ; 着弾フラグ } sx1=int(sx(scn))-so2 ; ショット左座標 sx2=int(sx(scn))+so2 ; ショット右座標 sy1=int(sy(scn))-so2 ; ショット上座標 sy2=int(sy(scn))+so2 ; ショット下座標 repeat eb ; 敵との当たり判定 if ef(cnt)=0 : continue ex1=int(ex(cnt))-eo2 ; 敵左座標 ex2=int(ex(cnt))+eo2 ; 敵右座標 ey1=int(ey(cnt))-eo2 ; 敵上座標 ey2=int(ey(cnt))+eo2 ; 敵下座標 if sx1<ex2 and ex1<sx2 and sy1<ey2 and ey1<sy2 { sf(scn)=-1 ehp(cnt)- ; 耐久度- if ehp(cnt)=0 { ; 倒したか ef(cnt)=0 : ; 当たった敵とショットを消す scr+ ; スコア if hiscr<scr : hiscr=scr ; ハイスコアの更新 break } } loop if sf(scn)=1 : if sx2<0 or wx<sx1 or sy2<0 or wy<sy1 : sf(scn)=0 : break ; 場外でフラグ off if sf(scn)=-1 : break ; 着弾してる loop ; ==================== loop ; ENEMY ---------- repeat 1 ; 敵出現 if ec>0 : ec- : break ; ウエイト中 repeat eb ; フラグ確認 if ef(cnt)>0 : continue ; 使用中 ef(cnt)=1 ; 使用フラグon ehp(cnt)=ehpb r=rnd(4) ; 出現方向 if r=0 : exs=rnd(wx)-eo2 : eys=0-eo2 ; 上 if r=1 : exs=rnd(wx)-eo2 : eys=wy+eo2 ; 下 if r=2 : exs=-eo2 : eys=rnd(wy)-eo2 ; 左 if r=3 : exs=wx+eo2 : eys=rnd(wy)-eo2 ; 右 ex(cnt)=double(exs) ; 初期位置x ey(cnt)=double(eys) ; 初期位置y xn=double(jx-exs) ; 敵と自機の距離x yn=double(jy-eys) ; 敵と自機の距離y rad=atan(xn,yn) ; 距離xyから角度を求める edx(cnt)=sin(rad)*esp ; 角度とスピードから移動量xを求める edy(cnt)=cos(rad)*esp ; 角度とスピードから移動量yを求める er(cnt)=-rad+pai ; 向き ec=ecb ; 出現ウエイト break ; 登録終わり loop loop repeat eb ; 敵移動処理 if ef(cnt)=0 : continue ; 無し ex(cnt)+=edx(cnt) : ey(cnt)+=edy(cnt) ; 移動 ex1=int(ex(cnt))-eo2 ; 敵左座標 ex2=int(ex(cnt))+eo2 ; 敵右座標 ey1=int(ey(cnt))-eo2 ; 敵上座標 ey2=int(ey(cnt))+eo2 ; 敵下座標 if jx1<ex2 and ex1<jx2 and jy1<ey2 and ey1<jy2 { mainf=2 : break ; 自機と当たりで GAME OVER } if ex2<0 or wx<ex1 or ey2<0 or wy<ey1 : 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 jx,jy : celput jw ; 自機 repeat eb ; 敵 if ef(cnt)=0 : continue ; 無い pos ex(cnt),ey(cnt) : celput ew,,,,er(cnt) ; 敵 loop repeat sb ; ショット if sf(cnt)=0 : continue ; 無い pos sx(cnt),sy(cnt) if sf(cnt)=-1 : celput hw,,,,sr(cnt) : sf(cnt)=0 : continue ; フラグリセット celput sw ; ショット loop pos x,y : celput cw ; カーソル switch mainf case 0 ; タイトル pos wx/2-120,wy/2-50 : mes "1画面防衛シューティング" pos wx/2-43,wy/2+30 : mes "var.0.004" 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 ; 終わり
カーソルのグラ(少し大きくした)
自機、ショット、敵は前回と同じ。
プログラム解説
ショットの処理
skc=3 ショットを 1フレームに移動する回数。 su=32 xx=x-su/2+rnd(su) ; ショットの着弾点x カーソル座標に半分値を -して全値ランダムを + dim sxb,sb ; 終点座標x sxb(cnt)=xx : syb(cnt)=yy ; 終点座標 着弾地点座標。 ddim skb,sb ; 最終移動距離 x2=xx-jx : y2=yy-jy sqrt = ルートの値を出す HSPの命令です。 xx=ショット終点座標、jx=始点座標です。 最初に全移動距離を算出するが、これは 2点間の距離を測る方法、ピタゴラスの定理を使う。 a2乗=b2乗+c2乗 → a=√(b × b)+ (c × c) 直角三角形の斜辺の2乗は、横の長さの 2乗 + 縦の長さの 2乗に等しいとかなんとか。 これをベタに書くと sqrt((xx – jx) × (xx – jx) + (yy – jy) × (yy – jy)) となるが見難いので上記のようにしてる。 これで 2点間の距離が分かる、例によって数学的な事は自分には良く分からないのでググってね。 移動距離(skn)が全距離(skb)を超えたかどうかで着弾したかどうか判定する。 その場合ショットの座標を最終座標にし、フラグを着弾したとする値 -1にする。 -1は表示の時に着弾グラを表示させるのに使い、着弾表示したらその場で 0にしてフラグを消す。 |
敵の処理
ehpb=40 ; 耐久度 敵の最大耐久数。 dim ehp,eb ; 耐久度 ショットが当たった時に -1して行き、0になったら破壊。 |