1MAP_STG ver.0.0008 プログラムの解説


2019年 5月 4日
 
 
 それではプログラムの解説をします。
 
 
1、ショットの当たり判定を刷新した

2、レーザーの照射面積によるダメージ変化

3、レーザーを撃とう

4、レーザーを表示する

5、被弾表示
 
 

1、ショットの当たり判定を刷新した

 前回 verまでは、ショットの大きさ以上の速さで動かすと敵との当たり判定ですり抜けるので、 1フレーム毎に「スピード ÷ 大きさ」回、移動と当たり判定を繰り返してました。

 具体的には 1フレームに 3回、移動させて都度当たり判定をしてます。

 今回。線と円との当たり判定の新技術により、これを 1回で済むように直してます。

 これはバルカンも同じです。

#deffunc s_syo ; 移動処理

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

	 sx1=sx(sn) ; 始点座標x
	 sy1=sy(sn) ; 始点座標y

	 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 ; 着弾フラグ
	  }

	 sx2=sx(sn) ; 終点座標x
	 sy2=sy(sn) ; 終点座標y

	 sln=sqrt((sx2-sx1)*(sx2-sx1)+(sy2-sy1)*(sy2-sy1)) ; 線の長さ


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

;dbg_put
	 if ef(en)=1 : eo2=e1o2			; 敵1の時の大きさ
	 if ef(en)=2 : eo2=e2o2*12/10	; 敵2の時の大きさ
	 if ef(en)=3 : eo2=e3o2 		; 敵3の時の大きさ
	  eo2+=so2 ; 敵の半径にショットの半径を足す

	  f=0
	  repeat 1 ; 当たり判定

; 両端判定
	   if (ex(en)-sx2)*(ex(en)-sx2)+(ey(en)-sy2)*(ey(en)-sy2)<=(eo2*eo2) : f=1 : break ; 終点と敵の当たり判定
	   if (ex(en)-sx1)*(ex(en)-sx1)+(ey(en)-sy1)*(ey(en)-sy1)<=(eo2*eo2) : f=1 : break ; 始点と敵の当たり判定


; ショット軌道と敵との当たり判定

	   sxn=ex(en)-sx1	; 敵x-始点x
	   syn=ey(en)-sy1	; 敵y-始点y

	   cosf=cos(-sr(sn)) ; 角度修正、垂直に
	   sinf=sin(-sr(sn)) ; 角度修正、素直に

	   xf=cosf*sxn-sinf*syn ; 垂直位置に修正した敵のx
	   yf=sinf*sxn+cosf*syn ; 垂直位置に修正した敵のy

	   if abs(yf)<=eo2 and 0<=xf and xf<=sln : f=1 ; 当たり判定
	  loop


; 当たり処理

	  if f=1 {
	   sf(sn)=-1				; 着弾フラグ
	   eaf(en)=1				; 被弾フラグ
	   ehp(en)-=sp				; 耐久度-
	   if ehp(en)<=0 {			; 倒したか
	    scr+=escr(ef(en))		; スコア
	     if hiscr<scr : hiscr=scr ; ハイスコアの更新

	    ef(en)=0				; 敵を消す
	     if en=esn : esn_set 0	; 次の標的を探す
	    vc+=vck					; バルカンの残弾数を増やす
	     if vc>vcb : vc=vcb		; 最大数
	   }
	  }
	 loop

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

	return

 
 
 ショットの移動軌道の線と、敵とショットの合計の半径の円とで当たり判定をします。

 線と円の当たり判定については前の記事、
 1MAP_STG 当たり判定の研究編 を参照して下さい。
 
 

 これで移動分の当たり判定を 1回行えば済みます。

 ショットの厚さ半径は 3ドッくらいですが、これを敵の大きさの半径に足せば、ショットの厚さも考慮して判定できます。

 要するに敵の大きさをショットの半径分、増してやれば良い訳です。

 もしこれをショットの矩形(ショット幅)と敵の円で処理すると、矩形と円の当たり判定という別の技術が要る事になります。

 敵の円が線に接していて、始点と終点より敵中心の x軸が線より外にあると判定漏れになるので、始点と終点と判定大きさの円とで、つまり点と円の当たり判定もします。
 
 
 
2、レーザーの照射面積によるダメージ変化

 レーザーが太いし、単に当たっているだけで同じダメージ量だと、敵要塞のような耐久力の大きい敵に対してダメージが低くてゲームバランスが悪いので、レーザーの当たっている面積で(実際には幅で)ダメージが変化するようにした。


 
 
 具体的には当たり判定時の回転修正後の yf座標が、レーザー中央線軸に対する敵の距離、つまり照射面積(幅)そのままなので、それを利用する。

 dn=eo2 – abs(int(yf)) ダメージ

 eo2 = 敵の半径とレーザーの半径を足した数

 それに対して、敵の各大きさを最大ダメージにする。

 つまり小さな 14ドットの敵は最大 14ダメージ、64ドットの要塞なら最大 64ダメージが入る。
 
 
 
3、レーザーを撃とう

 レーザーも別のゲームで何回か作った事があるが、これが意外と難しかったりする。

 いつもは単に連続で連なるショットとして作っていたが、今回は 1本のみとなる。
 
 
 まず基本的に進行状態によるモードを 3つくらいに分ける。

1、先頭が端まで伸びるモード
2、長さがいっぱいのモード
3、後尾が縮むモード
 
 
 今回は、

lf=0 無し
lf=1 発射要求、発射セット(出現)
lf=2 先頭が伸びるモード
lf=3 エネルギー消費まで伸び切ったモード
lf=4 後尾が縮むモード
lf=5 連続発射防止のためのウエイト

 となってる。

; LASER --------------------
#deffunc l_set ; 出現

	if lf!1 : return ; 発射要求されてない

	if le<leb : lf=0 : return ; エネルギーが無い

	lf=2 : ;le-=leb		; 発射


	lx1=double(jx) : ly1=double(jy) ; 始点座標
	lx2=lx1 : ly2=ly1	; 終点座標

	xn=lxs-lx1			; 自機と目標地点の距離x
	yn=lys-ly1			; 自機と目標地点の距離y

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

	ldx=cos(lr)*lsp		; 角度とスピードから移動量xを求める
	ldy=sin(lr)*lsp		; 角度とスピードから移動量yを求める

	return


#deffunc l_syo ; レーザー移動処理

	if lf<2 or lf=5 {
	 if le<leb : le+ ; エネルギー蓄積
	}

	if lf=2 or lf=3 : le- ; エネルギー消費

	if lf<2 : return ; 発射されてない


; レーザー移動処理

	switch lf
	 case 2 ; 先頭が伸びる
	  lx2+=ldx ; レーザー終点座標x
	  ly2+=ldy ; レーザー終点座標y

	  if lx2<-20 or wx+20<lx2 or ly2<-20 or wy+20<ly2 : lf=3 ; 先頭が画面外
	 swbreak

	 case 3 ; 伸びきってる
	 ;if lc>lcb : lf=4 ; 発射時間を過ぎた
	  if le=0 : lf=4 ; エネルギー切れ
	 swbreak

	 case 4 ; 後尾が縮む
	  lx1+=ldx ; レーザー終点座標x
	  ly1+=ldy ; レーザー終点座標y

	  if lx1<-20 or wx+20<lx1 or ly1<-20 or wy+20<ly1 : lf=5 : lc=0 ; 後尾が画面外
	 swbreak
	 case 5 ; ウエイト
	  lc+ : if lc>20 : lf=0
	 swbreak
	swend

	lln=sqrt((lx2-lx1)*(lx2-lx1)+(ly2-ly1)*(ly2-ly1)) ; 線の長さ


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

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

	 eo2+=int(double(lo2)*lopx)		; 敵の半径にレーザーの半径を足す

	 f=0
	 repeat 1 ; 当たり判定
; 両端判定
	  if (ex(en)-lx2)*(ex(en)-lx2)+(ey(en)-ly2)*(ey(en)-ly2)<=(eo2*eo2) : f=1 ;: break ; 終点と敵の当たり判定
	  if (ex(en)-lx1)*(ex(en)-lx1)+(ey(en)-ly1)*(ey(en)-ly1)<=(eo2*eo2) : f=1 ;: break ; 始点と敵の当たり判定


; レーザー軌道と敵との当たり判定

	  lxn=ex(en)-lx1		; 敵x-始点x
	  lyn=ey(en)-ly1		; 敵y-始点y

	  cosf=cos(-lr)			; 角度修正、垂直に
	  sinf=sin(-lr)			; 角度修正、素直に

	  xf=cosf*lxn-sinf*lyn ; 垂直位置に修正した敵のx
	  yf=sinf*lxn+cosf*lyn ; 垂直位置に修正した敵のy

	  if abs(yf)<=eo2 and 0<=xf and xf<=lln : f=1 ; 当たり判定
	 loop


; 当たり処理

	 repeat 1
	  if f=0 : break

	  eaf(en)=1					; 被弾フラグ

	  dn=eo2-abs(int(yf))		; 照射面積によるダメージ
	   if ef(en)=1 : if dn>e1o : dn=e1o ; 大きさによる最大ダメージ
	   if ef(en)=2 : if dn>e2o : dn=e2o
	   if ef(en)=3 : if dn>e3o : dn=e3o

	  ehp(en)-=dn				; 耐久度-
	   if ehp(en)>0 : break		; 倒したか

	  scr+=escr(ef(en))			; スコア
	   if hiscr<scr : hiscr=scr	; ハイスコアの更新

	  ef(en)=0					; 敵を消す
	   if en=esn : esn_set 0	; 次の標的を探す
	 loop

	loop

	return

 
 
 レーザーも、始点(lx1,ly1)、終点(lx2,ly2)となっているので、

1、先頭が伸びる時は lx2,ly2(終点座標)を移動させ
2、伸びきってる状態は座標変化無し
3、後尾が縮む時は lx1,ly1(始点座標)を移動させる

 という事でできた。
 
 
 
4、レーザーを表示する

 レーザーが少し難しいのは、特に表示においてだったりする。

 連続ショットのレーザーの場合は、例えば 1フレームに全レーザーの x座標を、全て自機の x座標にリセットしたりする(縦の場合のみ)

 今回は 1本のみなのでどう表示したものか悩んだが、celputの仕様で閃いたので、それを利用した。

 celputは x座標、y座標の拡大率を指定できる。
 そこでレーザーの長さを y座標の拡大で表示する事にした。

 表示座標(lx,ly)はレーザー線の中点となる。

 lr = 角度

 y拡大率 = lln(長さ)/ lo(レーザーのグラ yドットの大きさ)

 なお celdivにより、表示はレーザーグラの中心点を基点としている。

#deffunc l_put ; レーザー表示

	if lf<2 or lf=5 : return ; 発射されてない


	lln=sqrt((lx2-lx1)*(lx2-lx1)+(ly2-ly1)*(ly2-ly1)) ; レーザーの長さ

	lx=lx1+cos(lr)*lln/2 ; 表示座標x、始点座標に半分の長さを進んだ中点座標
	ly=ly1+sin(lr)*lln/2 ; 表示座標y

	pos lx,ly

	gmode 5,,,255 ; 半透明加算表示
	 celput lw,,lopx,lln/lo,lr+rad90 ; レーザー

	return

 なお、今回 atanの使い方を正規の atan(ly2-ly1,lx2-lx1)としたので、celputのラジアン(回転向き)は上が 0°で、atanのラジアンは右が 0°のため、表示向きには+rad90(rad90には1.5707965が入れてある)、つまり 90°回転が足されている。
 
 
 
5、被弾表示

 今回レーザー照射がされている描写が欲しくて、被弾表示(つまり弾が当たっている敵の表示)を考えた。

1、まず gmode 2で普通に表示する
2、gmode 5,,,255、つまり加算合成でもう一度同じ座標に同じものを表示する。

 これでその表示物は明るい表示になる。

 今回はこの技術をレーザー枠の選択表示(発射モード)にも使っている。


 
 
 
 という事で以上、今回は初めて「線と円の当たり判定」導入による、レーザーの実装でした。
 
 
 またしばらく HEXTRATEGY専門なので、完成までは 1MAP_STGの方はお休みだと思います。
 
 

コメントを残す

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