追跡カメラ

追跡カメラ

 いわゆるFPSの3人称視点でのカメラワークです。
キャラクターの後ろをカメラが追いかける感じです。
これまでのサンプルでも、キャラクターの後ろにカメラを固定して実装していましたが、今回はちょっと工夫してみました。


結果から言うと、失敗です。…3D酔いします。_| ̄|○
では、どんなものか見ていきましょう。

追跡方法

 これまでの追跡方法は、キャラクターの後頭部側の固定位置にCCDカメラをつけたようなイメージでした。
そのため、キャラクターは常に後頭部しか見えず、キャラクターの見栄えがあまりよくありませんでした。
今回はキャラクターの横顔などがよく見えるように考えてみました。

 キャラクターを追いかけ、カメラの視点は常にキャラクターを中心にとらえるという方針はこれまでとさほど変わりありません。
変更したのは追跡方法です。

 方法は、カメラはPCと一定距離を保ちつつ、PCに引っ張られるようにして移動します。
 考え方としては、車の前輪と後輪のような関係です。車の後部座席の窓から少し見を乗り出して、タイヤの前輪を見るようなイメージを考えてもらえばいいと思います。 ハンドルが切られるたびに、前輪の横側を見ることが出来ますよね。そういうイメージです。

3D酔い

 実際にサンプルを動かしてみるとわかると思うのですが、結構酔えます。これまでの後頭部固定型のカメラよりも画面の動きは少ないはずなのですが、結構気分悪くなってきます。
 この問題を解決するには酔いのメカニズムを知る必要があるようです。
少し調べたところによると、3D酔いには個人差はあるもののプレイヤーの予想外の方向に画面が動くと3D酔いがおきやすいようです。
だとすると結構納得できるものがあります。これまでの後頭部固定型だとプレイヤーの動作とまったく同じに画面が動いていたので、プレイヤーにも画面の動きを予測できていました。 しかし、今回の追尾システムではちょっと予想がしにくくなっています。これが原因の一因でしょう。
さらに、カメラを上下にふることが多いようなので、もしかするとそれが効いているのかもしれません。

 もう少しカメラを固定ぎみに調整してやるといいのかもしれませんね。

サンプル

【注意】このサンプルをプレイすることによって3D酔いを引き起こす危険性があります。
このサンプルをプレイすることによって、3D酔いを起こすなど健康に障害が発生したり、何らかの不具合が生じても、GENKIは責任を一切負いません。
必ず自己責任でプレイしてください。また、3D酔いしやすい体質の方はプレイしないようお願いします。


ということで、サンプルです。
いつものようにタコのデータは付属サンプルのものを使用しています。
操作は、カーソルキーで移動、スペースでジャンプです。

;
;	追跡カメラ
;

#include "e3dhsp3.as"

	;/////////////////
	;
	;	初期化
	;
	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		;キー入力
	fkabe = 0	;壁データとの当たり判定(0:off / 1:on)
	accy = 0.0
	vely = 0.0
	fmidair = 1	;空中フラグ(0:地表、1:空中)
	grav = -2.0


	;/////////////////
	;
	; ライトの設定
	;
	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	;モーションは使用しない。


	;/////////////////
	;
	;	形状データのロード
	;
	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
	E3DCreateQ axisqid_cam


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


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

		if keybuf.VK_ESCAPE = 1 : goto *bye ; [ESC]で終了
		gosub *MoveChara		;キャラクター移動
		gosub *ChkConf			;地面との当たり判定(キャラクター移動の後に実行)
		gosub *MoveCamera		;カメラ移動

		;不透明部分の描画をする
		E3DRender scid1, hsid0, 0, frameno0, 0	;バックバッファにレンダリングする。(地面描画)
		E3DRender scid1, hsid1, 0, frameno1, 0	;バックバッファにレンダリングする。(タコ描画)
		;半透明部分の描画をする
		E3DRender scid1, hsid1, 1, frameno1, 0, 0, 0, 1

	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.0
	mdegstep = -degstep

	E3DGetDirQ2 hsid1, axisqid

	if keybuf.VK_UP    = 1 {
		E3DPosForward hsid1, forwardstep	;矢印上
	}
	if keybuf.VK_DOWN  = 1 {
		E3DPosForward hsid1, backstep		;矢印下
	}
	if keybuf.VK_LEFT  = 1 {
		E3DRotateQLocalY axisqid, mdegstep	;矢印左
	}
	if keybuf.VK_RIGHT = 1 {
		E3DRotateQLocalY axisqid, degstep	;矢印右
	}
	E3DSetDirQ2 hsid1, axisqid
	if (keybuf.VK_SPACE = 1)&(fmidair = 0) {
		vely = 80.0		;ジャンプ力
	}
	E3DGetPos hsid1, posx, posy, posz
	vely += accy	;速度計算
	posy += vely	;座標計算
	E3DSetPos hsid1, posx, posy, posz	;空中

	return


;/////////////////
;
;	カメラ移動(キャラクター追跡型)
;
*MoveCamera
	;キャラクターの後ろを自動追尾する
	;	キャラクターの地面に落ちた影を追いかけているイメージです。
	E3DGetPos hsid1, saveposx1, saveposy1, saveposz1
	E3DSetPosOnGround hsid1, hsid0, mapheight,, saveposx1, saveposz1
	E3DGetPos hsid1, saveposx2, saveposy2, saveposz2
	E3DSetPos hsid1, saveposx1, saveposy1, saveposz1
	E3DGetCameraPos cmpposx, cmpposy, cmpposz
;	saveposy2 = saveposy1	;コメントを外すとキャラクターを追尾します。

	dx = cmpposx - saveposx1
	dy = cmpposy - (saveposy2+1400.0)
	dz = cmpposz - saveposz1
	E3DVec3Length dx,dy,dz, len
	E3DVec3Normalize dx,dy,dz, nx,ny,nz
	clen = 3000.0
	cmpposx = nx*clen + saveposx1
	cmpposy = ny*clen + (saveposy2+1400.0)
	cmpposz = nz*clen + saveposz1

	E3DSetCameraPos cmpposx, cmpposy, cmpposz
	E3DSetCameraTarget saveposx1, saveposy2+1200.0, saveposz1, 0.0,1.0,0.0
	return


;/////////////////
;
;	キャラクタの地面との当たり判定
;
*ChkConf
	;	地面データとキャラクター
	mapminy = -100	;地面データのY座標の最小値。実際の最小値より、少し小さな値を入れる。
	E3DChkConfGround hsid1, hsid0, 1, mapheight, mapminy, mterrain, adjustx, adjusty, adjustz, nx, ny, nz
	E3DGetPos hsid1, posx, posy, posz
	if (posy>adjusty)|(mterrain=0) {
		accy = grav
		fmidair = 1	;空中フラグon
	} else {
		accy = 0.0
		vely = 0.0
		fmidair = 0	;空中フラグoff
		E3DSetPos hsid1, adjustx, adjusty, adjustz	;地面の上に移動
	}
	return