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画面モードだ。
表示も同じやり方で、前よりスッキリしたと思う。
