1MAP-STG 敵種類を複数化しよう


2018年 6月22日
 
 
 ver.0.007


 
 今回はまとめたものを zipファイルにしました。

 表示の 0.006を 0.007にし忘れてますが、最後に気づいてもうめんどくさいのでそのままです。

 プログラムやった事のある人なら分かってもらえると思いますけど、バグは必ずある(笑)
 
 
 今回は敵の種類を 3種類にした。

 バギーまたはバイクのイメージ

 戦車のイメージ

 要塞のイメージ

 今回はまだアルゴリズム(挙動のプログラム)自体は全部自機に向かう直線運動だけで変化は無い。
 
 
 合わせてソースが長くなったので、この辺でいつものフォーマット(プログラム構造)を使う事にした。


 
 
 こんな感じでソースファイルを分割する


 
 
 main.hspがメインソースで、ここに put.hsp、shot.hsp、ene.hsp、syoki.hspを #inculude(インクルード、取り込み、この場所にそのファイルのソースを置き換える)する。

 put=表示関係全部
 shot=ショット、サブウエポン
 ene=敵
 syoki=初期設定、及びゲームリセット

 と言うような感じで分割してる。
 
 
 ザックリ表にするとこんな感じ

 .hspはファイル、赤矢印は呼び出し。

 put、shot、eneのソースはラベルを使わず、全部 #deffuncで書かれている。

 なので前回やったが、HSPでは呼び出される #deffuncソースが呼び出し箇所の前に書かれなければいけないので、このような配置になってる。
 
 
 main.hspのソースはこんな感じ

; 企画第一弾 : 1画面防衛シューティングを作ろう
; var.0.007

#include "hsp3dish.as" ; HSPdishの宣言


gosub *main : end		; メインルーチン

#include "put.hsp"		; 表示
#include "shot.hsp"		; 弾
#include "ene.hsp"		; 敵
#include "syoki.hsp"	; 初期設定


*main ; メインルーチン

	gosub *syoki ; 初期設定

	mainf=0 ; メインフラグ

	while ; ---------- メインルーチン ----------

	 switch mainf
	  case 0 : gosub *titl		: swbreak ; タイトル
	  case 1 : gosub *game_main	: swbreak ; ゲーム
	  case 2 : gosub *game_over	: swbreak ; ゲームオーバー
	 swend

	 if mainf=3 : _break ; end
	wend

	return


*titl ; タイトル

	repeat
	 stick k : if k!0 : mainf=1 : break ; 何か押したらゲームスタート
	 redraw 0 ; 表示
	  clr		; 画面クリア
	  j_put		; 自機表示
	  c_put		; 照準表示
	  scr_put	; スコア表示
	  pos wx/2-120,wy/2-50 : mes "1画面防衛シューティング"
	  pos wx/2-43,wy/2+30 : mes "var.0.006"
	 redraw 1
	 await 1000/15
	loop

	return


*game_main ; ゲーム

	repeat ; ゲームループ ========================================

	 if mainf!1 : break ; ゲームオーバー

; 入力 ----------
	 x=mousex : y=mousey	; マウス、スマホの座標

	 stick k,256	; マウス、スマホクリック
	
	 repeat 1 ; バルカンの入力判定
	  if k=256 : kf=1 : kc+ : cx=double(x) : cy=double(y) : break ; 押している
	  if kf=0 : break				; 一度も押してない

	  kf=0							; 放した、押しフラグリセット
	  esn_set 1						; 近くの標的をセット
	  if kc>2 : kc=0 : break		; 2フレーム以上押していた
	  kc=0
	  if jx1<x and x<jx2 and jy1<y and y<jy2 : autof=(autof+1)\2 : esn_set 0 : break ; 自機の位置の場合、オート切り替え
	  if vc=0 or vcn>0 : break		; 弾切れ or バルカン発射中
	  vcn+=vnb
	 loop


; 照準 ----------

	 x2=ex(esn)-cx : y2=ey(esn)-cy
	  nd=sqrt(x2*x2+y2*y2); 標的と照準の距離

	 repeat 1 ; 照準の移動
	  if autof=0 : cx=double(x) : cy=double(y) : break ; マニュアルの場合

	  if nd<csp : cx=ex(esn) : cy=ey(esn) : break

; ↑ 標的が照準スピード以下の距離なら、照準座標を標的の座標にする
; ↓ そうでないなら照準スピード分動かす

	  xn=double(ex(esn)-cx) ; 一番近い敵と照準の距離x
	  yn=double(ey(esn)-cy) ; 一番近い敵と照準の距離y

	  rad=atan(xn,yn) ; 距離xyから角度を求める
	
	  cx+=sin(rad)*csp	; 照準移動x
	  cy+=cos(rad)*csp	; 照準移動y
	 loop


; 出現 ----------
	 s_set  ; ショット出現
	 v_set  ; バルカン出現
	 e1_set ; 敵1出現
	 e2_set ; 敵2出現
	 e3_set ; 敵3出現


; 移動処理 ----------
	 s_syo  ; ショット処理
	 v_syo  ; バルカン処理
	 e_syo	; 敵処理テーブル


	 redraw 0 ; 表示 ----------
	  clr		; 画面クリア
	  e_put		; 敵表示
	  j_put		; 自機表示
	  s_put		; ショット表示
	  v_put		; バルカン表示
	  c_put		; 照準表示
	  scr_put	; スコア表示
	 redraw 1

	 await 1000/15 ; ウエイト、FPS 15
	loop ; ====================================================

	return


*game_over ; ゲームオーバー

	repeat
	 stick k : if k!0 : mainf=0 : break ; 何か押したらタイトルへ

	 redraw 0 ; 表示
	  clr		; 画面クリア
	  e_put		; 敵表示
	  j_put		; 自機表示
	  s_put		; ショット表示
	  v_put		; バルカン表示
	  c_put		; 照準表示
	  scr_put	; スコア表示

	  pos wx/2-43,wy/2+50 : mes "GAME OVER"
	 redraw 1

	 await 1000/15 ; ウエイト、FPS 15
	loop

	reset ; ゲーム再初期化

	return

 
 
 while文(繰り返し)と switch文(値分岐)による mainf値分岐で、タイトルとメインゲームとゲームオーバーのモードを切り替えている。

 今回は表示関係を putに分離して、それぞれのモード事に表示プログラムを呼び出しで表示している。
 
 
 putのソース

; 表示

#deffunc clr ; 画面クリア

	color 1,1,1 : boxf : color 255,255,255

	return


#deffunc j_put ; 自機表示

	pos jx,jy
	 if autof=0 : celput jw1 ; 自機、マニュアル
	 if autof=1 : celput jw2 ; 自機、オート

	return


#deffunc e_put ; 敵表示

	repeat eb : en=cnt ; 敵No.
	 if ef(en)=0 : continue ; 無い
	 pos ex(en),ey(en)
	  if ef(en)=1 : celput e1w,,,,er(en) ; 敵
	  if ef(en)=2 : celput e2w,,,,er(en) ; 敵
	  if ef(en)=3 : celput e3w,,,,er(en) ; 敵
	loop

	return


#deffunc s_put ; ショット表示

	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

	return


#deffunc v_put ; バルカン表示

	repeat vb ; バルカン
	 if vf(cnt)=0 : continue ; 無い
	 pos vx(cnt),vy(cnt)
	  if vf(cnt)=-1 : celput hw,,,,vr(cnt) : vf(cnt)=0 : continue ; フラグリセット
	  celput vw,,,,vr(cnt) ; バルカン
	loop

	pos 10,wy-20 : mes vc ; バルカンの残弾表示
	repeat vc/10
	 pos 50+cnt*6,wy-12 : celput vw
	 if cnt=100 : break ; 最大
	loop

	return


#deffunc c_put ; 照準表示

	pos cx,cy : celput cw ; 照準

	return


#deffunc scr_put ; スコア表示

	pos wx-300,5 : mes "SCORE "+scr
	pos wx-150,5 : mes "HI SCORE "+hiscr

	return

 
 
 syokiのソース

*syoki ; 初期設定

	randomize	; 乱数の初期化

	pai=3.141593
	pai2=pai*2

	rad45=0.7853982		; ラジアンで45度
	rad90=1.5707965		; ラジアンで90度


	wx=640	; 画面の大きさx
	wy=360	; 画面の大きさy

	gmode 2

	jw1=1 : jo=20 : jo2=jo/2	; 自機、マニュアル
	 celload "jiki_3.png",jw1
	  celdiv jw1,jo,jo,jo2,jo2

	jw2=2						; 自機、オート
	 celload "jiki_2.png",jw2
	  celdiv jw2,jo,jo,jo2,jo2

	sw=3 : so=6 : so2=so/2	; ショット
	 celload "shot_1.png",sw
	  celdiv sw,so,so,so2,so2

	e1w=4 : e1o=20 : e1o2=e1o/2	; 敵1
	 celload "ene_1.png",e1w
	  celdiv e1w,e1o,e1o,e1o2,e1o2

	e2w=5 : e2o=24 : e2o2=e2o/2	; 敵2
	 celload "ene_2.png",e2w
	  celdiv e2w,e2o,e2o,e2o2,e2o2

	e3w=6 : e3o=64 : e3o2=e3o/2	; 敵3
	 celload "ene_3.png",e3w
	  celdiv e3w,e3o,e3o,e3o2,e3o2

	cw=7 : co=32 : co2=co/2	; カーソル
	 celload "csr_2.png",cw
	  celdiv cw,co,co,co2,co2

	hw=8 : ho=16 : ho2=ho/2	; ヒット
	 celload "hit_4.png",hw
	  celdiv hw,ho,ho,ho2,ho2

	vw=9 : vo=16 : vo2=vo/2	; バルカン
	 celload "vulcan_1.png",vw
	  celdiv vw,vo,vo,vo2,vo2


; 自機 ----------
	jx=wx/2	; プレイヤーの座標x
	jy=wy/2	; プレイヤーの座標y

	jx1=jx-jo2 ; 自機左座標
	jx2=jx+jo2 ; 自機右座標
	jy1=jy-jo2 ; 自機上座標
	jy2=jy+jo2 ; 自機下座標

	cx=double(jx) ; 照準座標x
	cy=double(jy) ; 照準座標y
	csp=20.0	  ; 照準スピード
	esn=0		  ; 標的

	autof=1	; オートフラグ(0=マニュアル、1=オート)


; 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			; 発射間隔(フレーム)
	sp=6			; パワー

	su=32			; 揺れ幅(集弾値)

; VULCAN ----------
	vb=30			; バルカン最大数
	 dim vf,vb		; バルカン発射フラグ
	 ddim vx,vb		; バルカン座標x
	 ddim vy,vb		; バルカン座標y
	 ddim vdx,vb	; バルカン移動量x
	 ddim vdy,vb	; バルカン移動量y
	 ddim vr,vb		; 向き

	vkc=3			; 1フレームに出現する数
	vsp=20.0		; スピード
	vp=5			; パワー
	vu=30			; 揺れ幅(集弾値)
	vnb=140			; 1クリックで発射される数
	vc=0			; 残弾数
	vcb=20			; 1つ敵を倒した時に増える数


; 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		; 向き

	dim escr,4		; スコア

	e1hpb=24		; 耐久度
	e1sp=1.0		; 敵1スピード
	e1c=0			; 出現カウント
	e1cb=10			; 出現間隔(フレーム)
	escr(1)=10		; スコア

	e2hpb=300		; 耐久度
	e2sp=0.5		; 敵2スピード
	e2c=50			; 出現カウント
	e2cb=70			; 出現間隔(フレーム)
	escr(2)=100		; スコア

	e3hpb=5000		; 耐久度
	e3sp=0.1		; 敵2スピード
	e3c=400			; 出現カウント
	e3cb=2000		; 出現間隔(フレーム)
	escr(3)=2000	; スコア

	scr=0		; スコア
	hiscr=0		; ハイスコア

	kf=0		; キーを押してるかフラグ
	kc=0		; 何フレームキーを押してるか

	return


#deffunc reset ; ゲーム再初期化

	cx=double(jx) ; 照準座標xリセット
	cy=double(jy) ; 照準座標yリセット

    repeat sb : sf(cnt)=0 : loop ; ショットリセット
	repeat vb : vf(cnt)=0 : loop ; バルカンリセット
	 vcn=0 : vc=0 ; 残弾リセット
	repeat eb : ef(cnt)=0 : loop ; 敵リセット

	scr=0 ; スコアリセット
	esn=0 ; 標的リセット

	return

 
 
 shotのソース

; ショット --------------------

#deffunc esn_set int esn_setf ; 標的設定

	esn=0		; 一番近い敵No.
	nd1=1000.0	; 一番近い距離

	repeat eb
	 if ef(cnt)=0 : continue

	 if esn_setf=0 : x2=ex(cnt)-jx : y2=ey(cnt)-jy ; 自機に近い
	 if esn_setf=1 : x2=ex(cnt)-cx : y2=ey(cnt)-cy ; 照準に近い

	  nd2=sqrt(x2*x2+y2*y2); 距離

	 if nd2<nd1 : nd1=nd2 : esn=cnt
	loop

	return


; SHOT --------------------
#deffunc s_set ; 出現

	if sc>0 : sc- : return ; ウエイト中

	 sc=scb ; 発射ウエイトリセット


	f=0 ; 空きフラグ確認 ----------
	repeat sb : sn=cnt ; ショットNo.
	 if sf(sn)=0 : sf(sn)=1 : f=1 : break ; 使用フラグon
	loop
	if f=0 : return ; 空きが無い


	sx(sn)=double(jx) : sy(sn)=double(jy) ; 初期座標

	xx=int(cx)-su/2+rnd(su) ; ショットの着弾点x
	yy=int(cy)-su/2+rnd(su) ; ショットの着弾点y

	xn=double(xx-jx)		; 自機とマウスの距離x
	yn=double(yy-jy)		; 自機とマウスの距離y

	rad=atan(xn,yn)			; 距離xyから角度を求める

	sr(sn)=-rad+pai			; 向き

	sdx(sn)=sin(rad)*ssp	; 角度とスピードから移動量xを求める
	sdy(sn)=cos(rad)*ssp	; 角度とスピードから移動量yを求める


	sxb(sn)=xx : syb(sn)=yy	; 終点座標

	skn(sn)=0.0				; 現移動距離をリセット

	x2=xx-jx : y2=yy-jy
	 skb(sn)=sqrt(x2*x2+y2*y2); 最終移動距離

	return


#deffunc s_syo ; 移動処理

	repeat sb : sn=cnt ; ショット移動処理
	 if sf(sn)=0 : continue ; 無し

	 repeat skc : cnt2=cnt ; 1ショットの移動繰り返し ====================
	  sx(sn)+=sdx(sn) ; xの移動
	  sy(sn)+=sdy(sn) ; yの移動

	  skn(sn)+=ssp ; どれだけ距離を進んだか
	   if skn(sn)>=skb(sn) { ; 終点判定
	 	 sx(sn)=double(sxb(sn))
		 sy(sn)=double(syb(sn))
		 sf(sn)=-1 ; 着弾フラグ
	   }

	  sx1=int(sx(sn))-so2 ; ショット左座標
	  sx2=int(sx(sn))+so2 ; ショット右座標
	  sy1=int(sy(sn))-so2 ; ショット上座標
	  sy2=int(sy(sn))+so2 ; ショット下座標


 ; 敵との当たり判定
	  repeat eb : en=cnt ; 敵No,
	   if ef(en)=0 : continue

	   if ef(en)=1 : eo2=e1o2 ; 敵1の時の大きさ
	   if ef(en)=2 : eo2=e2o2 ; 敵2の時の大きさ
	   if ef(en)=3 : eo2=e3o2 ; 敵3の時の大きさ

	   ex1=int(ex(en))-eo2 ; 敵左座標
	   ex2=int(ex(en))+eo2 ; 敵右座標
	   ey1=int(ey(en))-eo2 ; 敵上座標
	   ey2=int(ey(en))+eo2 ; 敵下座標

	   if sx1<ex2 and ex1<sx2 and sy1<ey2 and ey1<sy2 { ; 当たり判定
	    sf(sn)=-1	; 着弾フラグ
	    ehp(en)-=sp ; 耐久度-
	 	if ehp(en)<=0 { ; 倒したか
	 	 scr+=escr(ef(en)) ; スコア
	 	 ef(en)=0	; 当たった敵とショットを消す
	 	 esn_set 0	; 次の標的を探す
	 	 vc+=vcb	; バルカンの残弾数を増やす
	 	 if hiscr<scr : hiscr=scr ; ハイスコアの更新
	 	 break
	 	}
	   }
	  loop

	  if sf(sn)=1 : if sx2<0 or wx<sx1 or sy2<0 or wy<sy1 : sf(sn)=0 : break ; 場外でフラグ off

	  if sf(sn)=-1 : break ; 着弾してる

	 loop ; ====================
	loop

	return



; VULCAN --------------------
#deffunc v_set ; 出現

	repeat vkc : cnt2=cnt ; 1フレームに発射される数
	 if vcn=0 or vc=0 : break ; 発射要求数、または残弾が0

	 repeat vb : vn=cnt
	  if vf(vn)!0 : continue ; 使用中

	  vcn- ; 発射要求数-
	  vc- : if vc=0 : vcn=0 ; 残弾-

	  vf(vn)=1 ; 使用フラグon
	  vx(vn)=double(jx) : vy(vn)=double(jy) ; 初期座標

	  xn=cx-double(jx)		; 自機とマウスの距離x
	  yn=cy-double(jy)		; 自機とマウスの距離y

	  rd=double(-(vu/2)+rnd(vu+1))/100.0 ; 揺れ幅(ラジアン)
	   rad=atan(xn,yn)+rd	; 距離xyから角度を求める

	  vr(vn)=-rad+pai		; 向き

	  vdx(vn)=sin(rad)*vsp	; 角度とスピードから移動量xを求める
	  vdy(vn)=cos(rad)*vsp	; 角度とスピードから移動量yを求める

	  repeat cnt2
	   vx(vn)-=vdx(vn) ; 始点座標をバックさせておくx
	   vy(vn)-=vdy(vn) ; 始点座標をバックさせておくy
	  loop

	  break
	 loop
	loop

	return


#deffunc v_syo

	repeat vkc ; vkc回移動
	 repeat vb : vn=cnt ; バルカン移動処理
	  if vf(vn)=0 : continue ; 無し

	  vx(vn)+=vdx(vn) ; xの移動
	  vy(vn)+=vdy(vn) ; yの移動

	  vx1=int(vx(vn))-vo2 ; バルカン左座標
	  vx2=int(vx(vn))+vo2 ; バルカン右座標
	  vy1=int(vy(vn))-vo2 ; バルカン上座標
	  vy2=int(vy(vn))+vo2 ; バルカン下座標

	  repeat eb : en=cnt ; 敵との当たり判定
	   if ef(en)=0 : continue

	   if ef(en)=1 : eo2=e1o2 ; 敵1の時の大きさ
	   if ef(en)=2 : eo2=e2o2 ; 敵2の時の大きさ
	   if ef(en)=3 : eo2=e3o2 ; 敵3の時の大きさ

	   ex1=int(ex(en))-eo2 ; 敵左座標
	   ex2=int(ex(en))+eo2 ; 敵右座標
	   ey1=int(ey(en))-eo2 ; 敵上座標
	   ey2=int(ey(en))+eo2 ; 敵下座標

	   if vx1<ex2 and ex1<vx2 and vy1<ey2 and ey1<vy2 {
	    vf(vn)=-1
	    ehp(en)-=vp ; 耐久度-
	    if ehp(en)<=0 { ; 倒したか
	     scr+=escr(ef(en))	; スコア
	     ef(en)=0 ; 敵を消す
		 esn_set 1	; 次の標的を探す
	     if hiscr<scr : hiscr=scr ; ハイスコアの更新
	     break
	    }
	   }
	  loop

	  if vf(vn)=1 : if vx2<0 or wx<vx1 or vy2<0 or wy<vy1 : vf(vn)=0 : continue ; 場外でフラグ off

	  if vf(vn)=-1 : continue ; 着弾してる
	 loop
	loop

	return

 
 
 eneのソース

; ENEMY --------------------

#deffunc e1_set ; 出現

	if e1c>0 : e1c- : return ; ウエイト中

	 e1c=e1cb ; 出現ウエイトリセット


	f=0 ; 空きフラグ確認 ----------
	repeat eb : en=cnt ; 敵No.
	 if ef(en)=0 : ef(en)=1 : f=1 : break ; 使用フラグon
	loop
	if f=0 : return ; 空きが無い


	ehp(en)=e1hpb ; 耐久力


	r=rnd(4) ; 出現方向
	 if r=0 : exs=rnd(wx)-e1o2 : eys=0-e1o2		; 上
	 if r=1 : exs=rnd(wx)-e1o2 : eys=wy+e1o2	; 下
	 if r=2 : exs=-e1o2	  : eys=rnd(wy)-e1o2	; 左
	 if r=3 : exs=wx+e1o2 : eys=rnd(wy)-e1o2	; 右

	ex(en)=double(exs)	; 初期位置x
	ey(en)=double(eys)	; 初期位置y

	xn=double(jx-exs)	; 敵と自機の距離x
	yn=double(jy-eys)	; 敵と自機の距離y

	rad=atan(xn,yn) 	; 距離xyから角度を求める

	edx(en)=sin(rad)*e1sp	; 角度とスピードから移動量xを求める
	edy(en)=cos(rad)*e1sp	; 角度とスピードから移動量yを求める

	er(en)=-rad+pai	; 向き

	return


#deffunc e2_set ; 出現

	if e2c>0 : e2c- : return ; ウエイト中

	 e2c=e2cb ; 出現ウエイトリセット


	f=0 ; 空きフラグ確認 ----------
	repeat eb : en=cnt ; 敵No.
	 if ef(en)=0 : ef(en)=2 : f=1 : break ; 使用フラグon
	loop
	if f=0 : return ; 空きが無い


	ehp(en)=e2hpb ; 耐久力


	r=rnd(4) ; 出現方向
	 if r=0 : exs=rnd(wx)-e2o2 : eys=0-e2o2		; 上
	 if r=1 : exs=rnd(wx)-e2o2 : eys=wy+e2o2	; 下
	 if r=2 : exs=-e2o2	  : eys=rnd(wy)-e2o2	; 左
	 if r=3 : exs=wx+e2o2 : eys=rnd(wy)-e2o2	; 右

	ex(en)=double(exs)	; 初期位置x
	ey(en)=double(eys)	; 初期位置y

	xn=double(jx-exs)	; 敵と自機の距離x
	yn=double(jy-eys)	; 敵と自機の距離y

	rad=atan(xn,yn) 	; 距離xyから角度を求める

	edx(en)=sin(rad)*e2sp	; 角度とスピードから移動量xを求める
	edy(en)=cos(rad)*e2sp	; 角度とスピードから移動量yを求める

	er(en)=-rad+pai	; 向き

	return


#deffunc e3_set ; 出現

	if e3c>0 : e3c- : return ; ウエイト中

	 e3c=e3cb ; 出現ウエイトリセット


	f=0 ; 空きフラグ確認 ----------
	repeat eb : en=cnt ; 敵No.
	 if ef(en)=0 : ef(en)=3 : f=1 : break ; 使用フラグon
	loop
	if f=0 : return ; 空きが無い


	ehp(en)=e3hpb ; 耐久力


	r=rnd(4) ; 出現方向
	 if r=0 : exs=rnd(wx)-e3o2 : eys=0-e3o2		; 上
	 if r=1 : exs=rnd(wx)-e3o2 : eys=wy+e3o2	; 下
	 if r=2 : exs=-e3o2	  : eys=rnd(wy)-e3o2	; 左
	 if r=3 : exs=wx+e3o2 : eys=rnd(wy)-e3o2	; 右

	ex(en)=double(exs)	; 初期位置x
	ey(en)=double(eys)	; 初期位置y

	xn=double(jx-exs)	; 敵と自機の距離x
	yn=double(jy-eys)	; 敵と自機の距離y

	rad=atan(xn,yn) 	; 距離xyから角度を求める

	edx(en)=sin(rad)*e3sp	; 角度とスピードから移動量xを求める
	edy(en)=cos(rad)*e3sp	; 角度とスピードから移動量yを求める

	er(en)=-rad+pai	; 向き

	return


#deffunc e1_syo ; 敵1移動処理

	ex(en)+=edx(en) : ey(en)+=edy(en) ; 移動

	ex1=int(ex(en))-e1o2 ; 敵左座標
	ex2=int(ex(en))+e1o2 ; 敵右座標
	ey1=int(ey(en))-e1o2 ; 敵上座標
	ey2=int(ey(en))+e1o2 ; 敵下座標

	if jx1<ex2 and ex1<jx2 and jy1<ey2 and ey1<jy2 {
	 mainf=2 : return ; 自機と当たりで GAME OVER
	}

	if ex2<0 or wx<ex1 or ey2<0 or wy<ey1 : ef(en)=0 ; 枠外でフラグ off

	return


#deffunc e2_syo ; 敵2移動処理

	ex(en)+=edx(en) : ey(en)+=edy(en) ; 移動

	ex1=int(ex(en))-e2o2 ; 敵左座標
	ex2=int(ex(en))+e2o2 ; 敵右座標
	ey1=int(ey(en))-e2o2 ; 敵上座標
	ey2=int(ey(en))+e2o2 ; 敵下座標

	if jx1<ex2 and ex1<jx2 and jy1<ey2 and ey1<jy2 {
	 mainf=2 : return ; 自機と当たりで GAME OVER
	}

	if ex2<0 or wx<ex1 or ey2<0 or wy<ey1 : ef(en)=0 ; 枠外でフラグ off

	return


#deffunc e3_syo ; 敵3移動処理

	ex(en)+=edx(en) : ey(en)+=edy(en) ; 移動

	ex1=int(ex(en))-e3o2 ; 敵左座標
	ex2=int(ex(en))+e3o2 ; 敵右座標
	ey1=int(ey(en))-e3o2 ; 敵上座標
	ey2=int(ey(en))+e3o2 ; 敵下座標

	if jx1<ex2 and ex1<jx2 and jy1<ey2 and ey1<jy2 {
	 mainf=2 : return ; 自機と当たりで GAME OVER
	}

	if ex2<0 or wx<ex1 or ey2<0 or wy<ey1 : ef(en)=0 ; 枠外でフラグ off

	return


#deffunc e_syo ; 敵移動処理テーブル

	repeat eb : en=cnt ; 敵No,
	 if ef(en)=0 : continue ; 無し

	 switch ef(en)
	  case 1 : e1_syo : swbreak ; 敵1移動処理
	  case 2 : e2_syo : swbreak ; 敵2移動処理
	  case 3 : e3_syo : swbreak ; 敵3移動処理
	 swend
	loop

	return

 
 
 こんな感じで分割すると、敵とかショットの種類が増えても管理が分かりやすくなる。

 この構造論(フォーマット)を使えば、どんなゲームでも大体これで作れてしまう。

 自分は goto文は使わない、goto文は Javaとか Cではたぶん使われなくて、全部呼び出し returnで構成されてると思う、たぶん。
 
 

プログラム解説

メイン

while ; ———- メインルーチン ———-
 switch mainf
  case 0 : gosub *titl : swbreak ; タイトル
  case 1 : gosub *game_main : swbreak ; ゲーム
  case 2 : gosub *game_over : swbreak ; ゲームオーバー
 swend
 if mainf=3 : _break ; end
wend

 mainfの値を切り替える事により、モードを切り替える。

 OP、EDとか、ストーリーの挿入とか、戦闘前のパワーアップや面セレクトの待機画面とかの切り替えも考えられる。

 もう少し複雑なソースの場合、例えば RPGなんかだとゲームルーチン内にもう一つルーチンを作って、フィールド移動、町探索、戦闘なんかを切り替えたりする。
 
 
 キー入力について、今回はちょんと押すとバルカン発射だが、基本的に押した時点でカーソル位置がその場所にセットされるようにし、合わせてその場所から近い標的を再セットするようにした。

 前回そうしたつもりだったが、そうでもなかったみたい。


; 出現 ———-
 s_set ; ショット出現
 v_set ; バルカン出現
 e1_set ; 敵1出現
 e2_set ; 敵2出現
 e3_set ; 敵3出現
; 移動処理 ———-
 s_syo ; ショット処理
 v_syo ; バルカン処理
 e_syo ; 敵処理テーブル

 ショットと敵の出現と移動処理を、全部呼び出しで行う。


redraw 0 ; 表示 ———-
 clr ; 画面クリア
 e_put ; 敵表示
 j_put ; 自機表示
 s_put ; ショット表示
 v_put ; バルカン表示
 c_put ; 照準表示
 scr_put ; スコア表示
redraw 1

 表示関係も全て呼び出しで行う。

 
 
ene.hsp 敵

#deffunc e_syo ; 敵移動処理テーブル
 repeat eb : en=cnt ; 敵No,
  if ef(en)=0 : continue ; 無し
  switch ef(en)
   case 1 : e1_syo : swbreak ; 敵1移動処理
   case 2 : e2_syo : swbreak ; 敵2移動処理
   case 3 : e3_syo : swbreak ; 敵3移動処理
  swend
 loop

 ef(en)のフラグが 1なら敵 1、2なら敵 2、3なら敵 3の移動処理を実行する。

 要するに efとか ex,eyのような変数は、全部の敵で使いまわし。

つまり ef(n)=敵フラグ

 0=無し
 1=敵1
 2=敵2
 3=敵3

 と言う風になってる。

 移動処理を分けるのは今回は設定してないが、それぞれの敵でアルゴリズムが異なる場合を想定している。

 例えば小型は蛇行するとか、要塞から小型が吐き出されるとか、戦車からミサイルが放たれるとか、そういう要素(アルゴリズム)をそれぞれの処理ルーチンで作る。

 
 
syoki.hsp 初期

e1hpb=24 ; 耐久度
e1sp=1.0 ; 敵1スピード
e1c=0 ; 出現カウント
e1cb=10 ; 出現間隔(フレーム)
escr(1)=10 ; スコア
 
e2hpb=300 ; 耐久度
e2sp=0.5 ; 敵2スピード
e2c=50 ; 出現カウント
e2cb=70 ; 出現間隔(フレーム)
escr(2)=100 ; スコア
 
e3hpb=5000 ; 耐久度
e3sp=0.1 ; 敵2スピード
e3c=400 ; 出現カウント
e3cb=2000 ; 出現間隔(フレーム)
escr(3)=2000 ; スコア

 敵によってそれぞれ違うステータスを、ここで定義している。

 スコアについて、escrの配列変数に定義しおき、scr+=escr(ef(en))のように書けば、倒した敵のソースを分けずにスコア加算が出来る。

 escr(1)=10,100,2000 のような書き方でも良い。
 
 
#deffunc reset ; ゲーム再初期化
 
 cx=double(jx) ; 照準座標xリセット
 cy=double(jy) ; 照準座標yリセット
 
 repeat sb : sf(cnt)=0 : loop ; ショットリセット
 repeat vb : vf(cnt)=0 : loop ; バルカンリセット
  vcn=0 : vc=0 ; 残弾リセット
 repeat eb : ef(cnt)=0 : loop ; 敵リセット
 
 scr=0 ; スコアリセット
 esn=0 ; 標的リセット
 
 return

 再ゲームするにあたって、必要な初期化をするルーチン。

コメントを残す

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