モノに当たった弾丸

敵に当たったら弾丸消滅
▲敵に当たったら弾丸消滅
地面に当たったら跳ね返る
▲地面に当たったら跳ね返る

 前回の弾丸をただ飛ばすだけでは地面や的をすり抜けてしまうだけです。
これじゃ使い物になりませんね。
ということで、的や地面にあたったらそれなりの動きをするようにしてみます。

今回の弾丸の動作はこんな感じです。
・的(敵キャラ)にあたったら弾丸消滅。
・地面に当たったら跳ね返る。
普通は消滅ぐらいしか使わないので、反射はおまけですね。
それと今回は注意事項があります。E3DChkConfBillboardにバグがありますので、Easy3Dはver1.1.2.5以降を使用してください。

 ここまで来るとぐっとゲームらしくなってきます。(ぐっと重くなってきたような気もしないでもないのですが…。)
ここまでくれば移動射的ゲームぐらいは出来そうな感じです。



形状モデルと弾丸(ビルボード)

 ビルボードと形状モデルの衝突判定はE3DChkConfBillboardE3DChkConfBillboard2がいいでしょう。 ただし、どちらも境界球による判定なので形状モデルの形に注意しないと不自然な結果になります。
今回は特に問題もないのでE3DChkConfBillboardを使用しました。使い方を見てみましょう。

E3DChkConfBillboard hsid, confrate, result, confbbid, arrayleng, confnum

 hsid, confrate, resultあたりはだいたい理解できると思いますので説明省略。問題はこれ以降ですね。
confnumはhsidで指定した形状モデルに衝突しているビルボードの数が返ります。
confbbidは形状モデルに衝突しているビルボードのidが返ります。confbbidは配列変数で、衝突した複数のビルボードidが同時に格納されます。
arraylengは指定した形状モデルhsidに同時に衝突しうるビルボードの数を指定します。 confnum>arraylengになる、つまりarrayleng個以上同時に衝突するとエラーになるので注意が必要です。

 ということで、実際はこんな感じで使用します。

	arrayleng = 5
	dim confbbid, arrayleng		;ビルボード衝突判定用変数

というようにconfbbidの配列を確保します。衝突しうるであろう数の分だけ確保します。


	E3DChkConfBillboard hsid3, 1, result, confbbid, arrayleng, confnum
	if result ! 0 {
		repeat SHOTMAX
			i = cnt
			repeat confnum
				if bbid.0.i = confbbid.cnt : bbid.1.i = 0
			loop
		loop
	}

と、こんな感じで衝突したらそれなりの処理を加えます。
bbid.0.iは存在フラグとしています。0になった弾丸は消滅させます。

なお、弾丸の移動と消滅処理は、前回の場所から衝突判定後に移動してあります。


地面と弾丸(ビルボード)

ベクトル計算  地面に当たった場合の処理を行ないます。
普通は地面に当たったら弾丸は消滅するものですが、せっかく法線ベクトルが取得できるので跳ね返らせてみました。
跳ね返りの処理を消滅処理に置きかえれば普通の処理になりますね。

 弾丸の軌道と地面が交差しているかで衝突判定を行いました。
現在の位置と移動後の予測地点を結んだ線分と地面が衝突しているかE3DChkConfGround2を使って調べます。

 衝突後は跳ね返る動きの計算です。
右図を見ながら読んでください。壁への突入時の速度ベクトルをベクトルAとして、衝突後をベクトルBとします。
ベクトルBがほしいので、B = A + CということでCを求めます。
法線ベクトルが分かっているので、内積やらなにやら使ってごにょごにょと計算します…。
この辺は分かる人には分かるし分からない人にはぜんぜん分からないので、説明は省きます。(^_^;


	repeat SHOTMAX
		if bbid.1.cnt = 1 {
			E3DGetBillboardInfo bbid.0.cnt, x,y,z, texname,transparent,w,h		;ビルボードの座標取得
			;現在座標と移動後の予定座標との線分で計算する
			E3DChkConfGround2 x+bbid.3.cnt, y+bbid.4.cnt, z+bbid.5.cnt, x,y,z, hsid0, 0, mapheight, mapminy, result, ax,ay,az, nx,ny,nz
			if result = 1 {
;				z = 10 : E3DSetBillboardPos bbid.0.cnt, ax-(bbid.3.cnt/z),ay-(bbid.4.cnt/z),az-(bbid.5.cnt/z)	;なぜか地表の座標に移動するとうまくいかない場所がある。
				;地面の法線ベクトルから弾丸の反射方向ベクトルを計算する。
				E3DVec3Length bbid.3.cnt, bbid.4.cnt, bbid.5.cnt, ln1
				E3DDot nx,ny,nz, -bbid.3.cnt, -bbid.4.cnt, -bbid.5.cnt, 2*ln1, ln2
				E3DVec3Normalize nx,ny,nz, ln2, nx2,ny2,nz2
				bbid.3.cnt = nx2 + bbid.3.cnt
				bbid.4.cnt = ny2 + bbid.4.cnt
				bbid.5.cnt = nz2 + bbid.5.cnt
			}
		}
	loop

反射はなくてもいいようなお遊び機能なので分からなくても気にしないでください。
ちなみに判定対象を地面ではなく道の壁に設定するとなかなか面白い動きをしてくれます。


サンプル

 タコモデルを操作して射撃を行なうサンプルです。的にタコモデルを使用します。
最近スクリプトも長くなってきたので、解説のためあんまり関係なさそうなところをいくつか削除しています。
配布スクリプトは実行可能なように全スクリプト掲載していますのでご安心下さい。
操作はキーボードを使用します。操作方法はつぎのとおり。
キー入力 動作
カーソルキー(↑↓) 前後移動
カーソルキー(←→) 方向転換
スペース ジャンプ
Escキー 終了
Zキー 連続射撃
Xキー 単発射撃
スクリプトと必要なファイルはこちらです。→[e3d020.zip}
タコモデルのファイルtako.sigとタコ用テクスチャファイルtako01.bmpを用意しておいてください。


;
;	射撃するサンプル
;	 地面で反射
;	 敵に衝突すると消滅
;

#include "e3dhsp.as"

	;/////////////////
	;
	;	初期化
	;
	E3DInit
	dim keybuf, 256		;キー入力
	E3DCreateFont 24,12,400,,,,"MS ゴシック",fontid	;フォントの設定
	fkabe = 0	;壁データとの当たり判定(0:off / 1:on)
	g100 = -100	;重力加速度g×100 [単位/s^2](1.00/60 [単位/コマ^2])
	dim keybufon,256	;キーをトリガータイプで検出するためのフラグ(0:前回ループ時に押されていなかった、1:前回ループ時に押されていた)
	arrayleng = 5
	dim confbbid, arrayleng		;ビルボード衝突判定用変数



	;/////////////////	
	;
	;	カメラの初期化
	;	ライトの設定
	;
	【いつもどおりなので省略】

	;/////////////////	
	;
	;	形状データのロード
	;
	sdim mediadir, 2048
	mediadir = curdir + "\\tako.sig"
	;PC
	E3DSigLoad mediadir, hsid1
;	E3DSetPos hsid1, 5000, 0, 5000
	E3DSetPos hsid1, 5000, 500, 5000
	E3DSetBeforePos hsid1

	;NPC
	E3DSigLoad mediadir, hsid3
	E3DSetPos hsid3, 5000, 0, 1000
	E3DSetBeforePos hsid3

	E3DCreateQ axisqid

	;ビルボード作成
	sdim mediadir2, 2048
	mediadir2 = curdir + "\\shot01.bmp"		;適当なサイズのbmpファイルを指定してください。
	;画像サイズ取得
	buffer 2 : picload mediadir2
	px = winx : py = winy
	gsel 0, 1

	;モデル配置
	#define SHOTMAX 20			;弾丸予約数(120フレーム/6フレーム毎発=20発)
	#define SHOTLIFECOUNT 120	;弾丸寿命
	dim bbid, 6, SHOTMAX	;弾丸情報用配列変数(パラメータ、弾丸ストック)
		;bbid.0.id	ビルボードID
		;bbid.1.id	弾丸フラグ(0:OFF,1:ON)
		;bbid.2.id	寿命カウンタ(射撃後から経過したフレーム数をカウントする)
		;bbid.3.id	x方向速度
		;bbid.4.id	y方向速度
		;bbid.5.id	z方向速度


	;/////////////////	
	;
	;	地面の作成
	;
	【いつもどおりなので省略】

;////////////////////////////////////////////////////////////////

;/////////////////
;
;	メインループ
;
*main

	E3DGetKeyboardState keybuf	;キー状態取得
	if keybuf.VK_ESCAPE = 1 : goto *bye ; [ESC]で終了

	gosub *MoveChara		;キャラクター移動
	gosub *GravChr	;重力制御

	;バックバッファへの書き込み作業を行う
	E3DBeginScene	;-----シーンスタート
		;モデルが、視野内にあるか判定
		E3DChkInView hsid1	;キャラクター
		E3DChkInView hsid3	;NPC
		E3DChkInView hsid0	;地面
		E3DChkInView hsid2	;壁

		gosub *ChkShot	;弾丸のあたり判定と移動
		gosub *ChkConf	;地面との当たり判定(E3DChkInViewより後で呼ぶ)
		gosub *MoveCamera		;カメラ移動

		;バックバッファにレンダリングする。
		E3DRender hsid1, 0
		E3DRender hsid3, 0
		E3DRender hsid0, 0
		E3DRenderBillboard	;全ビルボードの描画

		;メッセージ表示
		textposy=0
		repeat confnum
			textposy += 20
			strchk8 = "hit billboard id :" + confbbid.cnt
			E3DDrawText 0, textposy, 1, 255,255,255, strchk8
		loop
		textposy = 40
		repeat SHOTMAX
			textposy+=14
			strchk8 = "("+cnt+")id="+bbid.0.cnt+", f="+bbid.1.cnt+", time="+bbid.2.cnt+" ( "+bbid.3.cnt+", "+bbid.4.cnt+", "+bbid.5.cnt+" )"
			E3DDrawText 0, textposy, 1, 255,255,255, strchk8
		loop
	E3DEndScene		;-----シーン終了
	E3DPresent		;バックバッファの内容を、プライマリバッファに転送。描画する。

	E3DSetBeforePos hsid1
	E3DSetBeforePos hsid3

	E3DWaitbyFPS 60 : await 0
goto *main

;////////////////////////////////////////////////////////////////
;
;	サブルーチン
;

;/////////////////
;
;	終了処理
;
*bye
	【いつもどおりなので省略】


;/////////////////
;
;	キャラクター移動
;
*MoveChara
	forwardstep = 100	;移動速度(前進)
	backstep = -forwardstep	;移動速度(後退)
	degstep = 5
	mdegstep = -degstep

	E3DGetDirQ2 hsid1, axisqid


	;	射撃
	;連射
	cnttrg++	;トリガーカウント
	if keybuf.'Z' = 1 {
		if cnttrg>=6 {		;6フレームごとに1回射撃(60フレームにつき10回射撃)
			gosub *make_shot
		}
	}

	;単発
	if keybuf.'X' = 1 {
		if keybufon.'X' = 0 {	;スペースキーが前回ループ時に押されていなかった場合
			gosub *make_shot
		}
		keybufon.'X' = 1
	} else {
		keybufon.'X' = 0
	}

	;	ジャンプ
	if keybuf.VK_SPACE = 1{
		;地表にいるときと一番高くジャンプした点で加速できる。
		;地表にいるときだけジャンプできるようにしたかったが、うまく地表が検出できなかったので苦肉の策。
		if vely =0 : vely = 50
	}

	;	移動と方向転換
	if keybuf.VK_UP    = 1 : E3DPosForward hsid1, forwardstep	;矢印上
	if keybuf.VK_DOWN  = 1 : E3DPosForward hsid1, backstep	;矢印下
	if keybuf.VK_LEFT  = 1 : E3DRotateY hsid1, mdegstep	;矢印左
	if keybuf.VK_RIGHT = 1 : E3DRotateY hsid1, degstep	;矢印右

	;速度による移動計算
	E3DGetPos hsid1, posx, posy, posz
	E3DSetPos hsid1, posx+velx, posy+vely, posz+velz
;	title "速度ベクトル:"+velx+", "+vely+", "+velz

	return


;/////////////////
;
;	カメラ移動(キャラクター追跡型)
;
*MoveCamera
	【いつもどおりなので省略】


;/////////////////
;
;	キャラクタの地面との当たり判定
;
*ChkConf
	【いつもどおりなので省略】


;/////////////////
;
;	重力(gravity)
;
*GravChr
	【いつもどおりなので省略】


;/////////////////
;
;	弾丸作成
;
*make_shot
	;	弾丸作成
	;空いている配列を使用して弾丸を作成する
	repeat SHOTMAX
		if bbid.1.cnt = 0 {
			E3DCreateBillboard mediadir2, px*3, py*3, 1, bbid.0.cnt, 1, 1	;表示画像サイズの調整はここでやる。
			E3DSetBlendingMode -1, bbid.0.cnt,1
			E3DSetRenderState -1, bbid.0.cnt, D3DRS_LIGHTING, 0
			shotnum = cnt
			break
		} else {
			shotnum = -1
		}
	loop
	;弾丸の初期設定
	if shotnum ! -1 {
		;弾丸発射位置調整
		E3DGetPos hsid1, saveposx1, saveposy1, saveposz1
		E3DMultQVec axisqid, 0, 600, -700, x, y, z
		E3DSetBillboardPos bbid.0.shotnum, saveposx1+x, saveposy1+y, saveposz1+z
		;初速度設定
		E3DMultQVec axisqid, 0, 0, -500, vlctysid1x, vlctysid1y, vlctysid1z	;弾丸速度
		bbid.3.shotnum = vlctysid1x	;初速度設定
		bbid.4.shotnum = vlctysid1y
		bbid.5.shotnum = vlctysid1z
		bbid.1.shotnum = 1	;弾丸フラグon
		cnttrg = 0	;トリガーカウントリセット
	}
	return


;/////////////////
;
;	弾丸の衝突判定と移動処理
;
*ChkShot
	;弾丸との衝突判定
	E3DChkConfBillboard hsid3, 1, result, confbbid, arrayleng, confnum
	if result ! 0 {
		repeat SHOTMAX
			if bbid.0.cnt = confbbid.0 : bbid.1.cnt = 0
		loop
	}

	;弾丸にパラメータの内容を反映させる
	repeat SHOTMAX
		if (bbid.1.cnt = 1)&(bbid.2.cnt <= SHOTLIFECOUNT) {
			;	弾丸移動
			bbid.2.cnt++
			E3DGetBillboardInfo bbid.0.cnt, x,y,z, texname,transparent,w,h		;ビルボードの座標取得
			E3DSetBillboardPos bbid.0.cnt, bbid.3.cnt+x, bbid.4.cnt+y, bbid.5.cnt+z		;現在位置と速度から移動後の座標を計算
		} else {
			;	弾丸消滅
			if bbid.0.cnt!0 : E3DDestroyBillboard bbid.0.cnt	;弾丸削除
			bbid.0.cnt = 0
			bbid.1.cnt = 0	;弾丸フラグoff
			bbid.2.cnt = 0	;寿命カウンタリセット
			;速度ゼロ
			bbid.3.cnt = 0
			bbid.4.cnt = 0
			bbid.5.cnt = 0
		}
	loop


	;弾丸と地面との衝突判定とその後の動作
	repeat SHOTMAX
		if bbid.1.cnt = 1 {
			E3DGetBillboardInfo bbid.0.cnt, x,y,z, texname,transparent,w,h		;ビルボードの座標取得
			;現在座標と移動後の予定座標との線分で計算する
			E3DChkConfGround2 x+bbid.3.cnt, y+bbid.4.cnt, z+bbid.5.cnt, x,y,z, hsid0, 0, mapheight, mapminy, result, ax,ay,az, nx,ny,nz
			if result = 1 {
;				z = 10 : E3DSetBillboardPos bbid.0.cnt, ax-(bbid.3.cnt/z),ay-(bbid.4.cnt/z),az-(bbid.5.cnt/z)	;なぜか地表の座標に移動するとうまくいかない場所がある。
				;地面の法線ベクトルから弾丸の反射方向ベクトルを計算する。
				E3DVec3Length bbid.3.cnt, bbid.4.cnt, bbid.5.cnt, ln1
				E3DDot nx,ny,nz, -bbid.3.cnt, -bbid.4.cnt, -bbid.5.cnt, 2*ln1, ln2
				E3DVec3Normalize nx,ny,nz, ln2, nx2,ny2,nz2
				bbid.3.cnt = nx2 + bbid.3.cnt
				bbid.4.cnt = ny2 + bbid.4.cnt
				bbid.5.cnt = nz2 + bbid.5.cnt
			}
		}
	loop
	return






- HOME -

GHP(仮)