道の上をだけ移動できるようにする

 レースゲームやRPGではキャラクターが通れない部分と通れる部分がありますね。
今回はこれを実現してみます。
RPGなどでは、これから進む領域が進入可能かどうか調べて、進入不可能領域だったら進まない。というロジックを 使われると思うのですが、今回はちょっと違います。
移動可能領域とそれ以外の領域の間に見えない壁を作り、壁にぶつかったらそれより先には進まないようにします。

 Easy3Dでは簡単に壁を作ることが出来る命令が用意されています。また、壁とキャラクターの当たり判定を行う命令も あるので、壁衝突後の処理も楽に作ることが出来ます。


壁を作る

 移動可能領域は、BMP画像ファイルから自動生成します。
普通は移動可能領域は道の上であることがほとんどなので、地面を作成したときに使った道の画像ファイルを使ってもかまいません。 あるいは地面作成に使用した画像とは別に移動可能領域設定用の画像を用意してもどちらでもOKです。

 移動可能領域の生成には、E3DSetMovableArea命令を使用します。
設定方法は地面を作ったときとよく似ています。異なる点は、移動可能領域と移動不可能領域との間に作成する 見えない壁の高さを指定する点です。
この見えない壁の高さは、E3DLoadGroundBMP命令で指定した、地面の高さの最大値より大きい値を指定してください。

  作成する壁の高さ(wallheight) = 地面の高さの最大値(maxheight) + キャラクターが乗り越えられない高さ

です。
 地面の高さに応じて壁の高さが変化したりはしません。このため地面の高さの最大値よりも壁を低く作ると山の頂上を通ったときに 壁よりも上にキャラクターがいることになり、移動可能領域の外に出ることが出来るようになってしまいます。 これを回避するために壁は山より大きく作る必要があるわけです。


壁にぶつかる

 壁とキャラクターとのあたり判定には、E3DChkConfWall命令を使用します。

E3DChkConfWall charahsid, groundhsid, dist, result, adjustx, adjustx, adjustx, nx10000, ny10000, nz10000

 壁に衝突すると、resultに1が返されます。

 壁に衝突すると、キャラクターはdistだけ壁から内側に弾き飛ばされます。distは値が大きいと大きく跳ね返されます。
distの値を大きくしすぎると動きが不自然に見えてしまいます。また、跳ね返って移動された座標が移動可能領域の外になってしまう場合も あるので注意が必要です。
ちなみにマイナスにすると壁の外側に跳ね返されるので、移動可能領域が反転したような状態になります。
 distは壁にぶつかったときにどちら側に跳ね返るかを指定するだけのものと考えて、distの値は小さめにとって壁にぶつかった後に 大きく反射されるなどの処理については自力で処理したほうがいいでしょう。


E3DChkConfWall命令を使う前にE3DSetBeforePos命令でキャラクターの座標を保存しておく必要があります。


テキストを表示する

 今回のサンプルは、壁にぶつかったら分かるように文字を表示するようにしています。 ついでなので、文字列の表示についてもここでふれておきます。

 英数字のみの場合なら、E3DDrawText命令を使用すれば簡単にメッセージを表示できます。
 日本語の場合は、あらかじめE3DCreateFont命令でフォントを作成しておく必要があります。
表示するときはE3DDrawTextByFontID命令を使用します。
文字列を表示するときは、テキスト表示命令を挿入する位置に注意する必要があります。

 E3DBeginSceneE3DPresentの間に記述しないと、テキストは表示されません。  E3DRenderより前に実行するとE3DRenderで作成したモデルの後ろ側に表示されてしまいます。(テキストがモデルの影に隠れてしまう。)
これを利用すれば特定のモデル上にだけ文字列が表示するという妙な効果も出せます。(なんに使うのかはさておき。)(^_^;

 Easy3Dでは、ゲームに特化しているせいか、テキスト表示だけでもゲーム作成には嬉しい機能が搭載されています。例えば…
E3DDrawTextByFontIDWithCntを使うと、HSP2のTEXT命令とMES命令を組み合わせたときのような演出が出来ます。昔のRPGみたいなのですね。
E3DDrawTextByFontIDWithCnt2を使うと、文字を表示するタイミングを指定できます。最近のAVGのように、セリフに間やためを演出する といったことができるわけです。

こういうゲームでは基本的な動作のための命令が用意されているのはありがたいことです。


サンプル

壁に激突するサンプル 道の外がから内側には入れるが、道の外に出られないサンプルです。

タコモデルのファイルtako.sigとタコ用テクスチャファイルtako01.bmpを用意しておいてください。
実行に必要なファイル:e3d006.zip

大きな更新個所のみ赤色で表示しています。細かい変更点は自分で見つけてください。


;
;	キャラクターを移動する(道をはみ出さずに移動する)
;

#include "e3dhsp.as"

	;/////////////////
	;
	;	初期化
	;
	E3DInit
	dim keybuf, 256		;キー入力
	E3DCreateFont 24,12,400,,,,"MS ゴシック",fontid	;フォントの設定


	;/////////////////	
	;
	;	カメラの初期化
	;
	E3DSetProjection 100, 50000


	;/////////////////	
	;
	;	ライトの設定
	;
	E3DCreateLight lid1		;光源を作成
	lightdirx1 = 1		;平行光の向き
	lightdiry1 = -1
	lightdirz1 = 0
	lightr1 = 255	;平行光の色
	lightg1 = 255
	lightb1 = 255
	E3DSetDirectionalLight lid1, lightdirx1, lightdiry1, lightdirz1, lightr1, lightg1, lightb1	;光源を平行光源に設定


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


	;/////////////////	
	;
	;	地面の作成
	;
	sdim pathbuf, 2048, 4
	pathbuf.0 = curdir + "\\yama.bmp"	;地面の座標情報
	pathbuf.1 = curdir + "\\michi.bmp"	;地面の道の情報
	pathbuf.2 = curdir + "\\kawa.bmp"	;地面の川の情報
	pathbuf.3 = curdir + "\\bazou.bmp"	;地面、道、川の模様を決める、BMPファイル

	;地面作成用の値
	mapsize = 60000		;X,Z座標の最大値
	mapdiv = 120		;座標の分割数
	mapheight = 3000	;高さの最大値
	E3DLoadGroundBMP pathbuf.0, pathbuf.1, pathbuf.2, pathbuf.3, mapsize, mapsize, mapdiv, mapdiv, mapheight, hsid0

	;	壁の作成
	;道のマップをそのまま壁の生成に使います。
	E3DSetMovableArea pathbuf.1, mapsize, mapsize, mapdiv, mapdiv, mapheight+100, hsid2

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

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

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

	gosub *MoveChara		;キャラクター移動

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

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

		;バックバッファにレンダリングする。
		E3DRender hsid1, 0
		E3DRender hsid0, 0

	E3DEndScene		;-----シーン終了
	E3DPresent		;バックバッファの内容を、プライマリバッファに転送。描画する。

	E3DSetBeforePos hsid1

	E3DWaitbyFPS 60 : await 0
goto *main

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

;/////////////////
;
;	終了処理
;
*bye
	E3DDestroyLight lid1	;ライトを破棄
	E3DBye
	end



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

	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	;矢印右
	return


;/////////////////
;
;	カメラ移動(キャラクター追跡型)
;
*MoveCamera
	;キャラクターを後ろに2500移動して座標を取得。backpos
	E3DGetPos hsid1, saveposx1, saveposy1, saveposz1
	E3DPosForward hsid1, -2500
	E3DGetPos hsid1, backposx, backposy, backposz
	E3DSetPos hsid1, saveposx1, saveposy1, saveposz1
	;カメラをキャラクタ後方に設置
	E3DSetCameraPos backposx, backposy + 1500, backposz
	E3DSetCameraTarget saveposx1, saveposy1 + 800, saveposz1, 0, 1, 0
	return


;/////////////////
;
;	キャラクタの地面との当たり判定
;
*ChkConf
	; 壁(hsid2)とキャラクタ(hsid1)のあたり判定
	resultwall = 0
	E3DChkConfWall hsid1, hsid2, 10, resultwall, adjustx1, adjusty1, adjustz1, nx1, ny1, nz1
;	E3DChkConfWall hsid1, hsid2, -10, resultwall, adjustx1, adjusty1, adjustz1, nx1, ny1, nz1
	if resultwall!0 {
		E3DDrawTextByFontID fontid, 10,10,"壁に激突しました!", 255,255,255,255	;壁に激突したときのメッセージ。
		;E3DDrawText 10,10, 1, 255,255,255, "Collided with the wall !"	;テキストを簡易表示する場合。
		E3DSetPos hsid1, adjustx1, adjusty1, adjustz1
	}


	mapminy = -100	;地面データのY座標の最小値。実際の最小値より、少し小さな値を入れる。
	E3DChkConfGround hsid1, hsid0, 1, mapheight, mapminy, result, adjustx, adjusty, adjustz, nx, ny, nz
	if ( result != 0 ) {
		E3DSetPos hsid1, adjustx, adjusty, adjustz	;地面の上に移動
	}
	return




- HOME -

GHP(仮)