Unityにおいて一人称3Dゲームとしてキャラクターを動かす場合、
便利なコンポーネントが用意されている。
CharacterのFPS Input Controllerだ。
これはコンポーネントを組み込んだキャラクターをカメラの視点方向に合わせて
WASDキーによる移動ができるようになるというものだ。
上記の画像で言うと
Wキーで青方向前方に、Sキーで青方向後方に、
ADキーでそれぞれ赤方向左右に移動できる。
・・・と、ここまでだけなら非常に便利なコンポーネントなのだが
これにはひとつ注意すべきことがある。
それは 忠実すぎるほど向いてる方向に合わせて移動する ということだ。
これはどういうことかというと、上画像のように視点先が平行であれば特に問題ないのだが
下の地面を見ながらWキーで移動すると 地面にめりこむような移動を行おうとする。
実際は物理演算のお陰で地面にめり込むことはないのだが
その分前方向への移動量が下画像のように黄色の分だけになってしまう。
地面の上を走る分にはわかりづらいが、ジャンプして移動などすると非常にわかりやすい。
また、マウスによるキャラクターの視点先移動を使用せず
視点固定のままでFPS Input Controllerを使用した場合は症状が出ないため気づきにくい。
対策についてはFPS Input Controller.jsの中のソースをいじるしかない。
初期コードはjavascriptなので各自自分の使う種類のコードに自己変換すること。
// 視点の方向角に移動量(WASD)をかけることでXYZのそれぞれの移動量を算出
motor.inputMoveDirection = transform.rotation * directionVector;
初期状態では上記のような感じだが、ここを以下のようにする。
// 画面の中心を割り出し、そこから奥へ光線を飛ばす。
var centerPoint = Vector3(Screen.width/2, Screen.height/2, 0);
var ray = Camera.main.ScreenPointToRay(centerPoint);
// rayの変数に格納した視点先への角度ベクトルを
// yの方向ベクトルを0にすることでXZだけの水平の値にする。
var rot = Vector3(ray.direction.x, 0, ray.direction.z);
// そのままだと使えないので正規化(合算移動値が1になるように修正)する。
rot.Normalize();
// 上下角を水平にした視点先角度をクォータニオンに変換
var resultRot = Quaternion.LookRotation(rot);
// 視点方向に左右されない正しい移動量を代入
motor.inputMoveDirection = resultRot * directionVector;
上記の画像は元の視点先角度(黄色線)を水平に修正したもの(赤線)
宇宙空間や空中のようにどの方向にも進める場合は変更の必要はないが
陸の上を動く場合は注意が必要である。