はじめに

 hgimg4では、set系命令などが使えるようになれば移動は簡単です。 物理シミュレーションを有効にする前に、物理挙動がない状態でキー入力操作によるキャラクター(ノードオブジェクト)の移動を実装してみます。

円軌道で移動

 まずはサンプルを実行する準備です。 アヒルの素材を使用します。hgimg4のsampleフォルダからresフォルダをフォルダごとコピーして、作業フォルダに置いて下さい。

 自転するアヒルのサンプルをベースにサンプルを作成してみました。 実行するとアヒルが前に進みながら自転します。つまり円軌道を描いて移動します。


#include "hgimg4.as"

title "HGIMG4 前に移動"

	gpreset

	setcls CLSMODE_SOLID, $404040

	setcolor GPOBJ_LIGHT, 1,1,1		; ライトカラーを設定
	setdir GPOBJ_LIGHT, 0.5,0.5,0.5		; アンビエントカラーを設定

	gpload id_model,"res/duck"		; モデル読み込み
	setpos id_model, 0,0,0

	gpfloor id_floor, 40,40, $00ffff	; 床ノードを追加

	setpos GPOBJ_CAMERA, 0,20,20		; カメラ位置を設定

repeat
	stick key,15
	if key&128 : end

	redraw 0			; 描画開始

	gplookat GPOBJ_CAMERA, 0,0.3,0		; カメラから指定した座標を見る

	; ==========================================================================
	;	アヒルの顔が向いている方向に進む
	
	; アヒルを回転(Y軸回転)
	addang id_model, 0, 0.02
	
	; アヒル(ノード)の向きを取得します。
	; 初期値状態に比べてどのくらいの角度を向いているかを取得。
	getang id_model, xr, yr, zr
	fvset fvr, xr, yr, zr
	mes "向き   : " + rad2deg(fvr(0)) + ", " + rad2deg(fvr(1)) + ", " + rad2deg(fvr(2))

	; アヒルの正面は初期値では+X軸方向なので、
	; (0.1, 0, 0) 方向に進むようにします。(進行方向ベクトル)
	fvset fv_vel,  0.1, 0, 0
	; 進行方向ベクトルを現在のアヒル(ノード)の向きに回転します。
	fvmul fvr, -1,-1,-1		;fvdir のバグ対策
	fvdir fvr, fv_vel(0), fv_vel(1), fv_vel(2)
	mes "進行方向 : " + fvr(0) + ", " + fvr(1) + ", " + fvr(2)

	; 現在の向きに合わせた進行方向ベクトルを現在値に加算
	selpos id_model
	objaddfv fvr

	; ==========================================================================
	
	gpdraw				; シーンの描画

	color 255,255,255
	pos 8,8:mes "HGIMG4 sample"

	redraw 1			; 描画終了
	await 1000/60			; 待ち時間

loop

円軌道で移動の解説


	; アヒルを回転(Y軸回転)
	addang id_model, 0, 0.02

 addang命令で、アヒルの向きを変更します。Y軸のプラス方向に回転なので、少し左を向きます。


	; アヒル(ノード)の向きを取得します。
	; 初期値状態に比べてどのくらいの角度を向いているかを取得。
	getang id_model, xr, yr, zr
	fvset fvr, xr, yr, zr
	mes "向き   : " + rad2deg(fvr(0)) + ", " + rad2deg(fvr(1)) + ", " + rad2deg(fvr(2))

 getang命令で、アヒルが現在向いている向きを角度で取得します。アヒルの初期の向きとくらべて何ラジアン角度がついているかがわかります。


	; アヒルの正面は初期値では+X軸方向なので、
	; (0.1, 0, 0) 方向に進むようにします。(進行方向ベクトル)
	fvset fv_vel,  0.1, 0, 0

 アヒルが初期の向きの時、アヒルの顔が向いている方向は+X軸方向です。 アヒルを前に進める場合は、アヒルの顔が向いている方向がいいので進行方向も+X軸方向にします。 1フレームあたりに進む速さは、0.1 (=√(0.1^2 + 0^2 + 0^2))に設定しました。 前に進む方向と速度をサンプルでは便宜上「進行方向ベクトル」と呼称し、値は(0.1, 0, 0)としました。


	; 進行方向ベクトルを現在のアヒル(ノード)の向きに回転します。
	fvmul fvr, -1,-1,-1		;fvdir のバグ対策
	fvdir fvr, fv_vel(0), fv_vel(1), fv_vel(2)
	mes "進行方向 : " + fvr(0) + ", " + fvr(1) + ", " + fvr(2)

 アヒルは向きを変えるので、アヒルの向きに合わせて進行方向ベクトルも回転する必要があります。 fvdir命令を使って進行方向ベクトルをアヒルの現在の向きに回転しています。

 現在のバージョン(HSP3.6/3.7β1)では、fvdirは回転方向の符号が逆になる不具合があるため、fvmul命令でマイナスをかけて回転方向の符号を反転しています。 また引数の指定にもバグがあるため、引数は「向き,回転するベクトル」の順で指定しています。 計算結果は、向き情報が入っていた変数 fvr に回転後のベクトルが返されます。ややこしいですね。


	; 現在の向きに合わせた進行方向ベクトルを現在値に加算
	selpos id_model
	objaddfv fvr

 アヒルを前に移動します。現在の座標に、アヒルの向きに合わせた進行方向ベクトルを加算して移動します。「現在座標」+「移動量」=「移動後座標」というわけです。

 左に少し向きを変えて少し前に進むという動作を繰り返すので、結果として円軌道を左回転する動きをします。

キー操作で移動するサンプル

 移動ができるようになったので、次はキー操作で移動してみます。 アヒルをキーボードのカーソルキーで移動できます。


#include "hgimg4.as"

title "HGIMG4 操作"

	gpreset

	setcls CLSMODE_SOLID, $404040

	setcolor GPOBJ_LIGHT, 1,1,1		; ライトカラーを設定
	setdir GPOBJ_LIGHT, 0.5,0.5,0.5		; アンビエントカラーを設定

	gpload id_model,"res/duck"		; モデル読み込み
	setpos id_model, 0,0,0

	gpfloor id_floor, 40,40, $00ffff	; 床ノードを追加

	setpos GPOBJ_CAMERA, 0,20,20		; カメラ位置を設定

repeat
	stick key,15
	if key&128 : end

	redraw 0			; 描画開始

	fvset fv_vel, 0, 0, 0	; 進行方向ベクトル
	if key&1 : addang id_model, 0,  0.02	; ←左回転
	if key&4 : addang id_model, 0, -0.02	; →右回転
	if key&8 : fvset fv_vel, -0.1, 0, 0		; ↓進行方向ベクトル(後ろに進む)
	if key&2 : fvset fv_vel,  0.1, 0, 0		; ↑進行方向ベクトル(前に進む)

	gplookat GPOBJ_CAMERA, 0,0.3,0		; カメラから指定した座標を見る

	; ==========================================================================
	;	アヒルの顔が向いている方向に進む
	; ↓か↑が押されていない間は、無駄な処理になりますが
	; 今回はそのままにしてみました。
	
	; アヒル(ノード)の向きを取得します。
	; 初期値状態に比べてどのくらいの角度を向いているかを取得。
	getang id_model, xr, yr, zr
	fvset fvr, xr, yr, zr
	mes "向き   : " + rad2deg(fvr(0)) + ", " + rad2deg(fvr(1)) + ", " + rad2deg(fvr(2))

	; 進行方向ベクトルを現在のアヒル(ノード)の向きに回転します。
	fvmul fvr, -1,-1,-1		;fvdir のバグ対策
	fvdir fvr, fv_vel(0), fv_vel(1), fv_vel(2)
	mes "進行方向 : " + fvr(0) + ", " + fvr(1) + ", " + fvr(2)

	; 現在の向きに合わせた進行方向ベクトルを現在値に加算
	selpos id_model
	objaddfv fvr

	; ==========================================================================
	
	gpdraw				; シーンの描画

	color 255,255,255
	pos 8,8:mes "HGIMG4 方向キー:移動"

	redraw 1			; 描画終了
	await 1000/60			; 待ち時間

loop

解説

 先程の左旋回するアヒルのサンプルとあまり変わりません。


	if key&1 : addang id_model, 0,  0.02	; ←左回転
	if key&4 : addang id_model, 0, -0.02	; →右回転

 左右のキーを押している間は回転します。


	fvset fv_vel, 0, 0, 0	; 進行方向ベクトル
	…
	if key&8 : fvset fv_vel, -0.1, 0, 0		; ↓進行方向ベクトル(後ろに進む)
	if key&2 : fvset fv_vel,  0.1, 0, 0		; ↑進行方向ベクトル(前に進む)

 何も押さなければ進行方向ベクトルの大きさを 0 にすることで、移動しないようにしています。 上下キーを押すと進行方向ベクトルが増加・減少して前後に移動します。

 今回は何も押されていない時は、移動速度を 0 にするという実装方法にしました。 何も押していない間でもベクトルの回転などの必要のない処理が残ったままになっています。 無駄な処理だなと思いつつ、スクリプトを比較しやすいよう残してみました。

ジャンプするサンプル

 キー操作が出来るようになったので、ジャンプもしてみました。 スペースキーを押すとジャンプします。


#include "hgimg4.as"

title "HGIMG4 操作"

	gpreset

	setcls CLSMODE_SOLID, $404040

	setcolor GPOBJ_LIGHT, 1,1,1		; ライトカラーを設定
	setdir GPOBJ_LIGHT, 0.5,0.5,0.5		; アンビエントカラーを設定

	gpload id_model,"res/duck"		; モデル読み込み
	setpos id_model, 0,0,0

	gpfloor id_floor, 40,40, $00ffff	; 床ノードを追加

	setpos GPOBJ_CAMERA, 0,20,20		; カメラ位置を設定

	fvset fv_jump, 0,0,0	; ジャンプ速度
repeat
	stick key,15
	if key&128 : end

	redraw 0			; 描画開始

	fvset fv_vel, 0, 0, 0	; 進行方向ベクトル
	;fvset fv_jump, 0,0,0
	if key&1 : addang id_model, 0,  0.02	; ←左回転
	if key&4 : addang id_model, 0, -0.02	; →右回転
	if key&8 : fvset fv_vel, -0.2, 0, 0		; ↓進行方向ベクトル(後ろに進む)
	if key&2 : fvset fv_vel,  0.2, 0, 0		; ↑進行方向ベクトル(前に進む)
	if key&16 {
		if flgJump = 0 {			; ジャンプ中のジャンプを防止
			fvset fv_jump, 0, 1, 0	; ジャンプ初速
			flgJump = 1
		}
	}

	gplookat GPOBJ_CAMERA, 0,0.3,0		; カメラから指定した座標を見る

	; ==========================================================================
	;	アヒルの顔が向いている方向に進む
	; ↓か↑が押されていない間は、無駄な処理になりますが
	; 今回はそのままにしてみました。
	
	; アヒル(ノード)の向きを取得します。
	; 初期値状態に比べてどのくらいの角度を向いているかを取得。
	getang id_model, xr, yr, zr
	fvset fvr, xr, yr, zr
	mes "向き   : " + rad2deg(fvr(0)) + ", " + rad2deg(fvr(1)) + ", " + rad2deg(fvr(2))

	; 進行方向ベクトルを現在のアヒル(ノード)の向きに回転します。
	fvmul fvr, -1,-1,-1		;fvdir のバグ対策
	fvdir fvr, fv_vel(0), fv_vel(1), fv_vel(2)
	mes "進行方向 : " + fvr(0) + ", " + fvr(1) + ", " + fvr(2)

	; 現在の向きに合わせた進行方向ベクトルを現在値に加算
	selpos id_model
	objaddfv fvr

	; ジャンプ
	; 重力加速度の分だけジャンプ速度を減速
	if flgJump {
		selpos id_model
		objaddfv fv_jump
		fvsub fv_jump, 0, 9.8/60, 0
	}
	; 着地時の地面めり込み禁止
	; 地面からの距離がマイナスになった場合、地面にめり込んでいると判定する。
	selpos id_model
	objgetfv fv
	if fv(1) < 0.0 {
		fv(1) = 0.0
		objsetfv fv
		; ジャンプ終了
		fvset fv_jump, 0,0,0
		flgJump = 0
	}
	;mes "ジャンプ : " + fv_jump(0) + ", " + fv_jump(1) + ", " + fv_jump(2)
	; ==========================================================================
	
	gpdraw				; シーンの描画
	;mes "pos : " + fv(0) + ", " + fv(1) + ", " + fv(2)


	color 255,255,255
	pos 8,8:mes "HGIMG4 方向キー:移動/スペースキー:ジャンプ"

	redraw 1			; 描画終了
	await 1000/60			; 待ち時間

loop

解説

 キー入力で移動するサンプルをベースに作成しました。


	if key&16 {
		if flgJump = 0 {			; ジャンプ中のジャンプを防止
			fvset fv_jump, 0, 1, 0	; ジャンプ初速
			flgJump = 1
		}
	}
…
	; ジャンプ
	; 重力加速度の分だけジャンプ速度を減速
	if flgJump {
		selpos id_model
		objaddfv fv_jump
		fvsub fv_jump, 0, 9.8/60, 0
	}

 ジャンプ中は、flgJumpが1になります。着地している場合は、0です。 空中ジャンプを防止するため、ジャンプ中はジャンプ初速をリセットできないようにしています。

 fv_jumpをアヒルに加算することで、高さ方向に移動します。 ジャンプの瞬間の初速を1としています。この初速は、ジャンプ中(空中にいる間)は重力の働きを受けるため時間経過とともに減っていきます。 ある程度の時間が経過するとジャンプで上方向に移動する量がやがてマイナスになります。 マイナスY方向に移動する。つまり落下状態に入ります。


	; 着地時の地面めり込み禁止
	; 地面からの距離がマイナスになった場合、地面にめり込んでいると判定する。
	selpos id_model
	objgetfv fv
	if fv(1) < 0.0 {
		fv(1) = 0.0
		objsetfv fv
		; ジャンプ終了
		fvset fv_jump, 0,0,0
		flgJump = 0
	}

 落下し続けると地面の下まで落下し続けてしまうので、地面とアヒルの間の高さがマイナス(地面にめり込んだ状態)になったらジャンプを終了します。 そのままだと体の一部が地面にめり込んだままになってしまうので、アヒルの高さを地面と同じ高さに移動してジャンプ処理を終了します。

まとめ

 ベクトル操作がイメージできていればさほど難しい内容ではありませんでした。 言い換えるとベクトル操作がイメージできない人には難易度が高いかもしれません。 とにかく単純に前に進みたいだけなのに「べくとるをカイテン」とか言われてもわかんないですよね。 イラストで説明したいところなんですが、先へ進めたいので保留します。

 使い勝手が微妙と言いながらもMOC情報をサンプルで使ってみました。使いやすい側面も面倒な側面も両方あって微妙…。 objaddfvfvaddを使ってもどちらでも動作は変わりません。お好みで使い分けてください。