1MAP-STG ショットを複数にした


2018年 5月5日
 
 
 今回はショットを複数撃てるようにした。

 それかから switch文を使ってタイトル、ゲーム、ゲームオーバーのモードを分けるようにした。

 まだ 1ループプログラムだ。
 
 


 
 
 それではソースを

; 企画第一弾 : 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
 sb=ショット最大数、ショット最大数回分繰り返すという意味。

if sf(cnt)>0 : continue
 cntは repeatの値で繰り返し何回目か、1回目の値は0、2回目は1、3回目の値は2となり 1つずつ増える。
 sf=出現フラグで、0=出現してない、1=出現してる。
 配列変数 sf(cnt)が0でないならつまり既にその番は仕様中なら、continueこれは repeatを先頭に戻って繰り返して下さいという意味。

sf(cnt)=1
 出現フラグを onにする。

sx(cnt)=double(px) : sy(cnt)=double(py)
 初期位置をセット、px pyは自機の座標だが、ショットは自機からでるので。
 doubleはこの変数は実数(小数点を扱う)にするという意味。

xn=double(x-px)
yn=double(y-py)
rad=atan(xn,yn)
 x yはマウスの座標、マウスと自機(ショット初期位置)の角度を求めてます。
 詳しくは前回を参照して下さい。

sdx(cnt)=sin(rad)*ssp
sdy(cnt)=cos(rad)*ssp
 求めた角度から、ショットの移動量を求めてます。
 sdx sdy=ショット移動量、sspはショットスピードです。

break
 ここで repeatを抜ける(終わらせる)という意味で、一個ショットを設定したので抜けます。

sc=scb
 発射間隔をリセットしてます。

 
 
移動処理

scn=cnt
 repeat内で repeatを使ってるため、ショット用の cnt値を scnに渡して代用します。

sx(scn)+=sdx(scn)
sy(scn)+=sdy(scn)

 現座標に移動量を足して移動します。

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
 当たった場合は、その敵とショットのフラグを 0にして消します。

scr+
if hiscr<scr : hiscr=scr

 スコアを足して、ハイスコアより上ならハイスコアを上書きします。

break
 敵用の repeatから抜けます。

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
 場外判定、この場合画面の端(上下左右 4辺)より外ならフラグを off(0)にして消します。
 左端と上端は判定座標をキャラの大きさの分だけマイナスして判定します。
 そうしないと完全にキャラが枠外に出ません。

 
 
 さて、この仕様だとショットと敵の発生間隔が同じだった場合、百発百中だと延々とゲームが続く。

 敵を撃ちもらした分だけ包囲網が狭まるのだが、そこでショットの発生を敵より僅かに早くすると追い詰められてからの復帰ができる。

 この場合、敵のスピードが遅かったり、発生間隔が短いと簡単すぎてやはりゲームが終わらない。

 そこで敵のスピードを早くして、ミスを誘発するようにした。

 ゲームバランスを考えるのに一番時間がかかってる。
 
 
 mainf switch文にかけて、タイトル画面とゲームと GAMEOVER画面を振り分けてる。

 つまり mainf=0はタイトル、1=ゲームモード、2=GAMEOVER画面モードだ。

 表示も同じやり方で、前よりスッキリしたと思う。

コメントを残す

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