出オチタイトルっていいものですね。
1. 背景事情
タイトルにもある通り、VRで視界ジャックをやろうという話です。
ゲームの「SIREN」を知らない方のために端的に補足しておくと、 視界ジャックというのは要するに「他の人が見ている視界をそのまま覗く」ということです。
VR的な要件は以下のようになります。
- HMDの姿勢情報は使わずに捨てる: 人の視界を見るわけなので、HMDでユーザーの首振りや移動が分かってもフィードバックしてはいけません。
- 特定のターゲット(おもにキャラクターの頭部など)に対し、カメラの位置と姿勢を合わす
シンプルな要件ですが、それでも課題点があります。
実はUnityはVRモードになってるとカメラが勝手にHMDに合わせて動いてしまうので、 HMDの姿勢情報をわざわざ捨てるために若干の工夫が必要になります。
参考: Unity で VR モード時にカメラの位置を固定する
2. 実装とか
手元ではHTC Vive/Steam VRを使っており、以下のような実装にしました。
using UnityEngine; public class BindCameraToHead : MonoBehaviour { public Transform eye; public Transform target; void Update () { if(eye == null || target == null) { return; } var diffRot = target.rotation * Quaternion.Inverse(eye.localRotation); // 1 transform.position = diffRot * (-eye.localPosition) + target.position; // 2 transform.rotation = diffRot; // 3 } }
利用手順は以下の3ステップに分かれます。
[CameraRig]
をシーンに配置して上記スクリプト(BindCameraToHead.cs
)をアタッチeye
には、[CameraRig]
の子になってるCamera (eye)
を設定target
には、カメラ座標の基準にするゲームオブジェクトを設定
target
の座標系はXYZ軸が右、上、前に対応するようにしてください。
ヒューマノイドをターゲットにする場合、視界ジャックなので、目のあたりに空のオブジェクトを配置して、それをターゲットにすればOKです。
これで動かすと、target
で指定したオブジェクトの動きに合わせてHMDの映像が出ます。
上の画像の例でいくと、ユーザーの首の動きは無視され、ユニティちゃんの首の角度に応じてHMDに映像が流れてきます。 ようするにユニティちゃんが見てるのと厳密に同じ風景が見えてることになるので、考えようによっては非常に一体感が高いです。
視界ジャックと仰々しく言ってますが、主計算になっているのはたった3行です。
var diffRot = target.rotation * Quaternion.Inverse(eye.localRotation); // 1 transform.position = diffRot * (-eye.localPosition) + target.position; // 2 transform.rotation = diffRot; // 3
それぞれの考え方は以下の通りです。
- 回転に関しては、HMDの回転情報を打ち消して(
Quaternion.Inverse
の部分)、そのあとで本来のターゲットの回転に合わせる - 並進については、
eye
の位置を目標と一致させたいので、eye
を直接そこに持っていくかわりに[CameraRig]
をずれた位置に配置することで調節 - 1で求めた回転を適用してるだけ
なお注意点ですが、この方法を適用したコンテンツを長時間プレイするのは控えてください。死ぬほど酔います。
もし操作中に酔わなかった場合も、HMDを外した瞬間に死ぬほど酔います。
個人的な体感では、プレイ中よりHMD外した後の酔いの方が数倍キツいです。
3. 感想
当初うまくカメラ制御できずに悩んだのですが、 最終的にかなり短いコードで制御できたので満足です。
というのと、VR酔いコンテンツの制作体験になったので、 VR酔いに懲りる経験としては有効だったかなあと思います…。
4. ユニティちゃんライセンス
視界ジャックスクリプト自体は一般用途で使えるものなのでPublic Domain扱いですが、 記事中の画像にもあるように、検証時にはユニティちゃんを使ってたので載せておきます。
この作品はユニティちゃんライセンス条項の元に提供されています