自然な走行

自然な走行

 地面を走って、空中へジャンプしたり出来るようになったら、結構いろんなゲームが作れそうです。
しかし、3Dだけにファミコン時代のゲームのような動きには違和感を感じてしまいます。

というわけで。

 物理的な動きをゲーム内で再現してみました。
というか、以前タンクベータ(hgimg for HSP2)やペンペンパニック(E3D for HSP2)で使っていたものをEasy3D for HSP3用に移殖したものです。
若干の機能追加もしてあるのでよりいっそうリアルっぽい動きが実現できています。
もちろんモジュール化してあるので導入もそう難しくはないでしょう。

 実行サンプルは、空を飛ぶ所までは押さえておくと、スクリプトが理解しやすいと思います。

物理エンジンモジュール

 ということで、Easy3D用に物理シミュレーションを導入する専用モジュールを作りました。

物理表現用モジュール「iPS module」
ダウンロード[iPS.as]

名づけて「iPS module」(cyotto Iikagen Pysical Simulation Module)。
これをインクルードして、適当な命令を使うとEasy3Dで物理現象をちょっとだけ再現できるようになります。
しかし、ちょっといい加減なつくりなのであんまり厳密な物理現象とはいえませんのでご了承ください。

再現できるものは、次のとおりです。

  • 重力
  • 地面との摩擦
  • 空気抵抗(粘性抵抗)
  • 地面でのバウンド

主に地表の走行がリアルっぽくなるのでレースゲーム作るといい感じかもしれません。

iPSモジュール使い方

 e3dhsp.asをインクルードした後に、iPSモジュールをインクルードしてください。

#include "e3dhsp3.as"
#include "iPS.as"


キャラクターごとに速度の情報をもたせて、物理現象を再現します。

速度を操作する

モデルの速度を直接操作するには、PSSetVel命令とPSSetVel命令を使用します。
他にも用意していますが、これだけあれば十分でしょう。

PSSetVel:指定モデルIDの速度を設定

・解説
指定したモデルに速度を設定します。
書式はPSAddVelと同じです。

PSAddVel:指定モデルIDの速度に加算

PSAddVel hsid, vx, vy, vz

・引数
hsid: [IN] 数値または、変数
  モデルを識別する番号を指定してください。

vx: [IN] 数値または、変数
  モデルID=hsidのモデルの速度に加算する速度ベクトル(X成分)を指定してください。
vy: [IN] 数値または、変数
  モデルID=hsidのモデルの速度に加算する速度ベクトル(Y成分)を指定してください。
vz: [IN] 数値または、変数
  モデルID=hsidのモデルの速度に加算する速度ベクトル(Z成分)を指定してください。



速度情報を実際の動作に反映させるには、PSSetPos命令を使用します。
この命令を実行しないと、モデルが速度を持っていても実際に移動しません。

地球環境を反映させる

 速度だけでもあれば宇宙空間が再現できますが、地球には重力と空気抵抗があります。
ということで、これも再現します。

PSGrav:重力を再現する
  メインループ内で実行します。
  PSGrav命令を実行したら地面の情報を速度に反映させるため、E3DChkConfGround命令を実行した後にPSsakaVel命令を実行します。

PSsakaVel:速度に地面の状態を反映させる。

・解説
指定したモデルの速度に地面の状態を反映させます。

PSsakaVel hsid, nx, ny, nz, mu, mass, cr
・引数
hsid: [IN] 数値または、変数
  モデルを識別する番号を指定してください。
nx: [IN] 数値または、変数
ny: [IN] 数値または、変数
nz: [IN] 数値または、変数
E3DChkConfGround命令で取得した地面のポリゴンの法線ベクトル(nx, ny, nz)をそれぞれ指定します。

mu : [IN] 数値または、変数
   実数。摩擦係数
mass : [IN] 数値または、変数
   実数。キャラクターの質量
cr : [IN] 数値または、変数
   実数。跳ね返り係数、反発係数(0.0~1.0)



PSWinD:空気抵抗を再現する。
  メインループ内で実行します。


 PSGravおよびPSWinD命令実行後には必ずPSSetPos命令を 実行する必要があります。 ただしPSSetPos命令は1ループごとに2回以上実行してはいけません。 したがってPSSetPos命令は、PSSetVelおよびPSSetVelPSGravPSWinD命令を 実行したあとに1回だけ実行するようにしてください。


 各命令の詳細についてはサンプルスクリプトおよびモジュールのスクリプトをご覧下さい。
マニュアル作ってません。また、環境変数の変更はモジュールを直接書き換える必要があります。

サンプル

操作は、キーボードの矢印キーで操作します。
スペースキーで垂直上昇します。
加速のしすぎにご注意ください。
iPS.asをサンプルスクリプトと同じフォルダ内に入れておいてください。

実行に必要なファイル:e3d3004.zip

モジュール使用個所のみ赤色で表示しています。その他、細かい変更点は自分で見つけてください。

;
;	キャラクターを移動する
;		自然に見える走行
;

#include "e3dhsp3.as"
#include "iPS.as"	;物理表現用モジュール「iPS module」

	;/////////////////
	;
	;	初期化
	;
	wid = 0		;ウインドウID
	objid = -1	;オブジェクトID
	fullscreenflag = 0		;フルスクリーンフラグ
	bits = 16	;色数ビット数(fullscreenflag = 1 のときのみ有効)
	multisamplenum = 0		;アンチエイリアスのマルチサンプル数(0,2~16)
	;scid	スワップチェインID
	E3DInit wid, objid, fullscreenflag, bits, multisamplenum, scid
	dim keybuf, 256		;キー入力
	;フォントの設定
	E3DCreateFont 24,, 400, , , , msgothic, fontid
	fkabe = 0	;壁データとの当たり判定(0:off / 1:on)

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

	;/////////////////
	;
	;	カメラの初期化
	;
	camposx = 0 : camposy = 1000 : camposz = -4200
;	camposx = 0 : camposy = 0 : camposz = 4200	;デフォルト値
	E3DSetCameraPos camposx, camposy, camposz	;カメラ座標を設定
	targetx = 0		;注視点座標
	targety = 0
	targetz = 0
	upvecx = 0		;カメラの上方向のベクトル
	upvecy = 1
	upvecz = 0
	E3DSetCameraTarget targetx, targety, targetz, upvecx, upvecy, upvecz	;カメラの注視点を設定
	;プロジェクションの設定
	proj_near = 100.0 : proj_far = 50000.0 : proj_fov = 45.0
	E3DSetProjection proj_near, proj_far, proj_fov


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

	;地面作成用の値
	mapsize = 60000.0		;X,Z座標の最大値
	mapdiv = 60			;座標の分割数
	mapheight = 3000.0	;高さの最大値

	E3DLoadGroundBMP pathbuf.0, pathbuf.1, pathbuf.2, pathbuf.3, mapsize, mapsize, mapdiv, mapdiv, mapheight, hsid0
	posx0 = 0.0 : posy0 = 0.0 : posz0 = 0.0
	degx0 = 0.0 : degy0 = 0.0 : degz0 = 0.0
	E3DSetPos hsid0, posx0, posy0, posz0	;地面表示位置を設定
	E3DSetDir hsid0, degx0, degy0, degz0	;地面の向きを設定
	frameno0 = 0	;モーションは使用しない。

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


	;/////////////////
	;
	;	形状データのロード
	;
	sdim mediadir, 2048
	mediadir = dir_cur + "\\Media\\tako.sig"
	E3DSigLoad mediadir, hsid1
;	E3DSetPos hsid1, 13000, -1000, 16000
	E3DSetPosOnGroundPart hsid1, hsid0,, mapheight,, 13000, 16000
	E3DRotateY hsid1, 180
	frameno1 = 0	;モーションは使用しない。
	E3DSetBeforePos hsid1

	E3DCreateQ axisqid


;/////////////////
;
;	メインループ
;
*main
	E3DGetKeyboardState keybuf	;キー状態取得

	if keybuf.VK_ESCAPE = 1 : goto *bye ; [ESC]で終了
	gosub *MoveChara		;キャラクター移動
	PSGrav hsid1	;重力加速
	PSWinD hsid1	;空気抵抗
	PSSetPos hsid1	;速度による移動計算(キャラクター移動、重力加速、の後に記述する。)

	E3DBeginScene	;-----シーンスタート
		;モデルが、視野内にあるか判定
		E3DChkInView scid, hsid1	;PC
		E3DChkInView scid, hsid0	;地面
		E3DChkInView scid, hsid2	;壁

		gosub *ChkConf	;地面との当たり判定
		gosub *MoveCamera		;カメラ移動

		;不透明部分の描画をする
		E3DRender scid1, hsid0, 0, frameno0, 0	;地面
		E3DRender scid1, hsid1, 0, frameno1, 0	;PC
		;半透明部分の描画をする
		E3DRender scid1, hsid1, 1, frameno1, 0, 0, 0, 1	;PC

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

	E3DSetBeforePos hsid1

	E3DWaitByFPS 60, chkfps1 : await 0
	title ""+chkfps1 + " FPS"

goto *main


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


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

	;	方向変更
	if keybuf.VK_LEFT  = 1 : E3DRotateY hsid1, mdegstep	;矢印左
	if keybuf.VK_RIGHT = 1 : E3DRotateY hsid1, degstep	;矢印右

	;ジャンプ
	if keybuf.VK_SPACE = 1{
		PSAddVel hsid1, 0.0, 10.000, 0.0	;上方向へ増速
	}

	;キャラクターの向きに向かって進む速度ベクトルを作成し、その分だけ加速する。
	E3DGetDirQ2 hsid1, axisqid
	if keybuf.VK_UP {
		E3DMultQVec axisqid, 0.0, 0.0, -2.5, vlctysid1x, vlctysid1y, vlctysid1z
		PSSetAcc hsid1, vlctysid1x, vlctysid1y, vlctysid1z	;前方向に加速
	}
	if keybuf.VK_DOWN {
		E3DMultQVec axisqid, 0.0, 0.0, 2.5, vlctysid1x, vlctysid1y, vlctysid1z
		PSSetAcc hsid1, vlctysid1x, vlctysid1y, vlctysid1z	;後方向に加速
	}
	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
	;	壁データとキャラクター
	if fkabe ! 0 {
		; 壁(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 scid, fontid, 10,10,"壁に激突しました!", 255,255,255,255	;壁に激突したときのメッセージ。
			E3DSetPos hsid1, adjustx1, adjusty1, adjustz1
		}
	}

	;	地面データとキャラクター
	mapminy = -100	;地面データのY座標の最小値。実際の最小値より、少し小さな値を入れる。
	E3DChkConfGround hsid1, hsid0, 1, mapheight, mapminy, result, adjustx, adjusty, adjustz, nx, ny, nz
	if ( result != 0 ) {
		E3DGetPos hsid1, posx, posy, posz
		if posy>adjusty {
			E3DSetPos hsid1, posx, posy, posz	;空中
		} else {
			E3DSetPos hsid1, adjustx, adjusty, adjustz	;地面の上に移動
			PSsakaVel hsid1, nx, ny, nz, 0.80, 1.0, 0.5	;地面の傾斜に沿って移動
		}
	}
	return