空を飛ぶ

地上走行が出来るようになったので次は空を飛んでみます。

仕組みは簡単です。
地上走行の場合は、地面の上か下にいればとにかく地表の座標に移動する。 としていましたが空を飛行する場合は、
というふうにします。
わかりやすいように基本的な動作だけを考えることにするので、重力などは完全に無視しています。



キャラクターの操縦

 さて、空を飛行する場合もうひとつ問題になるのが、キャラクターの操縦方法です。

 方法はいくつかあります。ちょっと上げてみましょう。
 まず思いつくのが、飛行機のような操縦方法です。
ただこれは飛行機の操縦原理から考えなければならないのと、その再現で スクリプトが複雑になり、ここでの解説には向きません。また今の自分には 技術的に作れないとの理由から今回は取り上げません。

 次に一番簡単な方法を考えてみます。
今までのスクリプトに「高さ調節ボタン」を付け加える方法です。
押したら押し多分だけ高度上昇し、押したら押し多分だけ下に下がっていく、という2つ ボタンを作るわけです。これなら今までの説明の中の技術だけで実現できます。
ただ、十分な視界の確保が出来ないのが多少気になります。

 というわけで、今回の操縦は次のような感じで作っていきます。

最初の2つはこれまでと同じです。
左右旋回もローカル座標軸を使おうかと思ったんですが、意外と操縦しにくかったので止めました…。

ローカル座標  さて、問題は3番目のローカル座標軸ですね。
ここで言うローカル座標系は、操縦対象となるキャラクター(PC)が持つ座標系です。

 この座標系は、キャラクターが地面に対してどこを向うとも、キャラクターの向きと位置に 一致しています。 キャラクターと座標系の関係ですが、デフォルトでは-X方向を正面、+Y方向を上としています。(右図参照)
 この座標軸の向きとキャラクターとの関係は、キャラクターが地面に対してどっちを向いても かわりません。

 キャラクターに上下方向を向かせるには、これを利用します。
X軸方向に回転させれば、キャラクターが上下方向を向くことができますね。(ワールド座標だとうまくいかない理由は大丈夫ですね。)


※回転方向と軸との関係は別途各自で調べてください。
※ローカル座標とワールド座標(絶対座標)については、各自調べてください。
参考資料:HSP開発Wiki - 座標とピクセル



クォータニオン

 では、形状データの向き(キャラクターの向き)を取得してみます。
形状データの向きをベクトルで扱うのは面倒なのですが、Easy3Dはクォータニオンを使った形状データの向き(姿勢情報)を扱うための便利な機能をもっています。

 クォータニオン(クオタニオン、quaternion)とは計算・記述方法の一種で、ゲームではキャラクターを滑らかに回転させるために使用されるものです。
Easy3Dではクォータニオンを使用して、形状データの姿勢情報(回転情報)を書き換えることが出来ます。

 といっても、Easy3Dではクォータニオンを使用した計算部分は内部処理してくれるので、ユーザーはクォータニオンの計算方法について気にする必要はありません。
ここでは、クォータニオンIDとは姿勢情報(回転情報)を格納できる便利なもの、ぐらいの理解で十分でしょう。

姿勢情報の書き換えは次のような流れになります。

  1. クォータニオンIDを作成します。(E3DCreateQ
  2. 形状データの現在の姿勢情報をクォータニオンIDに格納します。(E3DGetDirQ2
  3. 形状データをローカル座標軸に関して回転します。(E3DRotateQLocalX,Y,Z
クォータニオンIDは1回作ればOKなのでループの外で作成してください。


サンプル

タコが地上を移動して山を登ったり空を飛んだり出来るサンプルです。
操作は、
矢印キー(左右):左右へ向きを変更。
矢印キー(上下):上下へ向きを変更。
Z:キャラクターが向いている方向へ、前進
X:キャラクターが向いている方向へ、後退
となっています。
タコモデルのファイルtako.sigとタコ用テクスチャファイルtako01.bmpを用意しておいてください。
実行に必要なファイル:e3d006.zip

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


;
;	キャラクターを移動する(空を飛ぶ、クォータニオン)
;

#include "e3dhsp.as"

	;/////////////////
	;
	;	初期化
	;
	E3DInit
	dim keybuf, 256		;キー入力
	E3DCreateFont 24,12,400,,,,"MS ゴシック",fontid	;フォントの設定
	fkabe = 0	;壁データとの当たり判定(0:off / 1:on)


	;/////////////////	
	;
	;	カメラの初期化
	;
	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


	E3DCreateQ axisqid


	;/////////////////	
	;
	;	地面の作成
	;
	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

	E3DGetDirQ2 hsid1, axisqid


	if keybuf.VK_UP    = 1 {
		E3DRotateQLocalX axisqid, -1,1	;矢印上
		E3DSetDirQ2 hsid1, axisqid
	}
	if keybuf.VK_DOWN  = 1 {
		E3DRotateQLocalX axisqid, 1,1	;矢印下
		E3DSetDirQ2 hsid1, axisqid
	}

	if keybuf.'Z'    = 1 : E3DPosForward hsid1, forwardstep	;z
	if keybuf.'X'    = 1 : E3DPosForward hsid1, backstep	;x

	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
	;	壁データとキャラクター
	if fkabe ! 0 {
		; 壁(hsid2)とキャラクタ(hsid1)のあたり判定
		resultwall = 0
		E3DChkConfWall hsid1, hsid2, 10, resultwall, adjustx1, adjusty1, adjustz1, nx1, ny1, nz1
		if resultwall!0 {
			E3DDrawTextByFontID 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	;地面の上に移動
		}
	}
	return




- HOME -

GHP(仮)