VR開発メモトップ

Unity+HTC Vive開発メモ

最終更新日:2017年11月14日

UnityでHTC Vive対応ソフトを開発する方法やTipsをまとめています。HTC Viveの概要やLighthouseについては、「HTC Viveについて」を参照してください。

更新履歴

(2017年11月14日)「Viveトラッカー編」の記述を増加、Unityの古いバージョンの記述を削除
(2017年6月14日)「位置トラッキングのモードを切り替えるには」にUnityのVRサポートでモード切り替える方法を追加、Steam VRプラグインのGitHubのリポジトリのリンクを修正、古いプラグインのバージョンの不具合についての項目を削除
(2017年3月29日)「Viveトラッカー編」をとりあえず作成、Unity 5.6でViveコントローラを認識しない問題の対策を追記
(2017年1月14日)SteamVRプラグインのトラッキングオブジェクト数について追記

目次


UnityでHTC Vive対応ソフトを作るには

UnityでHTC Vive対応ソフトを作るには、Asset StoreにあるSteamVRプラグインを使用します。新規プロジェクトを作成し、SteamVRプラグインをインポートしてください。

プラグインをインポートすると、SteamVR_Settingsというウィンドウが開きます。これによって、Unityのプロジェクトの設定がSteamVRの推奨設定に変更されます(64ビットビルド、非フルスクリーン、起動ダイアログを出さない、カラースペースをリニアにする等)。問題ないようでしたら、左下の「Accept All」をクリックしてください。「You made the right choice!(正しい選択をしたね!)」というノリノリのメッセージウィンドウが出ますので閉じます。

試しにサンプルシーンを開きます。Projectビューで、SteamVR/Scenes/exampleシーンを開き、再生してください。上手くいけば、HTC Viveの画面にたくさんの立方体とモーションコントローラが表示されているはずです。

また、既存のプロジェクト・シーンをHTC Vive対応にするには、カメラを無効化して、ProjectビューのSteamVR/Prefabs/[CameraRig]プレハブをシーンの床の中央となる位置に配置してください。

なお、Unityでシーンを再生する際にはGameビューの「Maximize on Play」をオンにしないと(エディタ画面の描画のため)フレームレートが大幅に低下しますので注意してください。


位置トラッキングについて

HTC Viveでは、ルームスケールもしくは着席状態のいずれかのトラッキングスペースを使用できます。デフォルトではルームスケールになっています。

トラッキングスペースを変更するには、SteamVR/Prefabs/[SteamVR]プレハブをシーンにドロップしてください。[SteamVR]のTracking SpaceでTracking Universe StandingまたはTracking Universe Seatedを選択します。

Tracking Universe Standing(デフォルト)

ルームスケールのモードです。[CameraRig]を配置した場所が、HTC Viveのルームセットアップで設定したプレイエリアの中央の地面になります。

通例、[CameraRig]を(0, 0, 0)等に配置してその周辺にシーンを構築することになるかと思います。[CameraRig]のTransformを変更するとプレイエリアを移動できます。

Tracking Universe Seated

着席モードです。[CameraRig]をVR空間のプレイヤーの頭の位置に配置します。Oculus Riftと同じようなモードです。

位置をリセットするには、SteamVRを起動した状態でヘッドセットをかぶって、Viveコントローラのシステムボタン(電源ボタン)を押しダッシュボードを表示します。右下にある設定ボタン>「一般的なVR設定」>「座位のリセット」でヘッドセットの位置・向きが[CameraRig]の位置・向きとしてアプリケーション間で保存されます。

また、SteamVR.instance.hmd.ResetSeatedZeroPose関数でもリセットできますので、これをキーやボタン等に割り当てると簡単です。サンプルスクリプトです。

using UnityEngine;

public class Recenter : MonoBehaviour
{
    void Update()
    {
        // 着席モードでRキーで位置トラッキングをリセットする
        if (Input.GetKeyDown(KeyCode.R)) {
            SteamVR.instance.hmd.ResetSeatedZeroPose();
        }
    }
}

なお、この関数はルームスケールのモードでは効果がないので注意してください。


Viveコントローラ編

Viveコントローラの位置を取るには

[CameraRig]プレハブ下にコントローラのゲームオブジェクト(Controller (left)、Controller (right))があり、Transformで位置や向きを取得できます。

また、SteamVR/Extrasに、コントローラでオブジェクトを投げるSteamVR_TestThrowというサンプルがあります。

トリガーやボタンの入力を取るには

トリガーやボタンは、コントローラのゲームオブジェクトにアタッチされているSteamVR_TrackedObjectコンポーネントからデバイスを参照して、GetTouch / GetPress関数で状態を、GetTouchDown / GetTouchUp / GetPressDown / GetPressUp関数で状態変化を取得できます。

以下にサンプルスクリプトを示します。ControllerExample.csという名前でController (left)またはController (right)にアタッチしてください。

using UnityEngine;

public class ControllerExample : MonoBehaviour
{
    void Update()
    {
        SteamVR_TrackedObject trackedObject = GetComponent<SteamVR_TrackedObject>();
        var device = SteamVR_Controller.Input((int) trackedObject.index);

        if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Trigger)) {
            Debug.Log("トリガーを浅く引いた");
        }
        if (device.GetPressDown(SteamVR_Controller.ButtonMask.Trigger)) {
            Debug.Log("トリガーを深く引いた");
        }
        if (device.GetTouchUp(SteamVR_Controller.ButtonMask.Trigger)) {
            Debug.Log("トリガーを離した");
        }
        if (device.GetPressDown(SteamVR_Controller.ButtonMask.Touchpad)) {
            Debug.Log("タッチパッドをクリックした");
        }
        if (device.GetPress(SteamVR_Controller.ButtonMask.Touchpad)) {
            Debug.Log("タッチパッドをクリックしている");
        }
        if (device.GetPressUp(SteamVR_Controller.ButtonMask.Touchpad)) {
            Debug.Log("タッチパッドをクリックして離した");
        }
        if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Touchpad)) {
            Debug.Log("タッチパッドに触った");
        }
        if (device.GetTouchUp(SteamVR_Controller.ButtonMask.Touchpad)) {
            Debug.Log("タッチパッドを離した");
        }
        if (device.GetPressDown(SteamVR_Controller.ButtonMask.ApplicationMenu)) {
            Debug.Log("メニューボタンをクリックした");
        }
        if (device.GetPressDown(SteamVR_Controller.ButtonMask.Grip)) {
            Debug.Log("グリップボタンをクリックした");
        }

        if (device.GetTouch(SteamVR_Controller.ButtonMask.Trigger)) {
            //Debug.Log("トリガーを浅く引いている");
        }
        if (device.GetPress(SteamVR_Controller.ButtonMask.Trigger)) {
            //Debug.Log("トリガーを深く引いている");
        }
        if (device.GetTouch(SteamVR_Controller.ButtonMask.Touchpad)) {
            //Debug.Log("タッチパッドに触っている");
        }
    }
}

トリガーの入力をアナログ値で取るには

GetAxis関数でトリガーのIDを指定して、戻り値のVector2のxで取得できます。サンプルスクリプトを示します。TriggerExample.csという名前でController (left)またはController (right)にアタッチしてください。

using UnityEngine;

public class TriggerExample : MonoBehaviour
{
    void Update()
    {
        SteamVR_TrackedObject trackedObject = GetComponent<SteamVR_TrackedObject>();
        var device = SteamVR_Controller.Input((int) trackedObject.index);

        float value = device.GetAxis(Valve.VR.EVRButtonId.k_EButton_SteamVR_Trigger).x;
        Debug.Log(value);
    }
}

戻り値は0から1の範囲になります。トリガーをクリック直前まで引くと0.83前後まで上がり、カチッとクリックすると1にジャンプするようです。ただし、コントローラーの個体によってはきちんと1にならないという話があります。また、トリガーを離しても完全に0に戻らないことがあります。

タッチパッドのタッチ位置を取るには

GetAxis関数で取得できます。サンプルスクリプトを示します。TouchpadExample.csという名前でController (left)またはController (right)にアタッチしてください。

using UnityEngine;

public class TouchpadExample : MonoBehaviour
{
    void Update()
    {
        SteamVR_TrackedObject trackedObject = GetComponent<SteamVR_TrackedObject>();
        var device = SteamVR_Controller.Input((int) trackedObject.index);

        Vector2 position = device.GetAxis();
        Debug.Log("x: " + position.x + " y: " + position.y);
    }
}

GetAxisの戻り値は、X軸が-1(左)から1(右)、Y軸が-1(下)から1(上)です。また、タッチパッドから指が離れていると(0, 0)が返ってきます。

GetPressDown (SteamVR_Controller.ButtonMask.Touchpad)と組み合わせて、どのあたりをクリックしたかを取ることもできます。端の方は取れないため注意です。

Viveコントローラの表示を消すには/違うオブジェクトを表示するには

コントローラのオブジェクトの子のModelにアタッチされているSteam VR Render Modelスクリプトのチェックを切って無効化します。

異なるオブジェクトを表示するには、Controller (left)またはController (right)に子オブジェクトをアタッチします。

Viveコントローラを振動させるには

TriggerHapticPulse関数を使います。引数で振動の大きさを指定します。最大3999まで機能しますが、100~2000ほどが有効範囲かなと思います(もう少し要調査)。

サンプルスクリプトを示します。HapticFeedbackExample.csという名前でController (left)またはController (right)にアタッチしてください。

using UnityEngine;

public class HapticFeedbackExample : MonoBehaviour
{
    void Update()
    {
        SteamVR_TrackedObject trackedObject = GetComponent<SteamVR_TrackedObject>();
        var device = SteamVR_Controller.Input((int) trackedObject.index);

        if (device.GetTouchDown(SteamVR_Controller.ButtonMask.Touchpad)) {
            // タッチパッドに触れた
            device.TriggerHapticPulse(500);
        }

        if (device.GetPress(SteamVR_Controller.ButtonMask.Trigger)) {
            // トリガーを深く引いている
            device.TriggerHapticPulse(1000);
        } else if (device.GetTouch(SteamVR_Controller.ButtonMask.Trigger)) {
            // トリガーを浅く引いている
            device.TriggerHapticPulse(100);
        }
    }
}

Viveコントローラを3つ以上使用するには

Viveコントローラは無線で2つまでしか(標準状態では)ペアリングできませんが、USBケーブルで接続すると3つ目以降のコントローラを使用できます。

SteamVRプラグインで3つ目以降のコントローラを認識・表示させるには、[Camera Rig]のController (left)またはController (right)を同じ階層に複製して、[CaneraRig]のSteam VR_Controller ManagerのObjecftsのSizeを0から増やして、複製したコントローラのオブジェクトを入れてください。

SteamVRプラグインでは、ヘッドセットと2つのViveコントローラーを含めて最大16オブジェクトまでトラッキングできるようです。


Viveトラッカー編

Viveコントローラーのほかに、小さいViveトラッカーが販売されています。これを物体等にくっつけることで位置を取得できます。カメラマウンタもついています。

Viveトラッカーをペアリングするには

トラッカーひとつにつき付属のドングルひとつが必要です。ドングルをPCに接続してください。できれば、付属のUSBケーブルとクレードルを使用してドングルをPCから離すと認識が良くなります。

トラッカー中奥の電源ボタンをクリックして電源を入れ、Steam VRのメニューで「デバイス>コントローラーのペアリング」を実行し、トラッカーの電源ボタンを長押しして青点滅状態にしてください。

ペアリングに成功するとSteam VRでトラッカーの形をしたアイコンが表示されます。

UnityでViveトラッカーを使用するには

Unityでは上記「Viveコントローラを3つ以上使用するには」と同じ方法で使用できます。

Viveトラッカーのステータスライトについて

ステータスライトの表示は以下のようになっています。

状態
接続(ペアリング済み)
未接続
青点滅 ペアリング中
赤点滅 バッテリー残量低下
オレンジ 充電中

電源ボタンの操作について

トラッカー中央の電源ボタンで以下の操作が可能です。

操作 効果
押す 電源オン
5秒間押す 電源オフ
5秒弱押す ペアリング(青点滅になる)
PCにUSB接続して10秒間押す ハードウェアリセット

その他

Steam VRを終了するとトラッカーの電源は自動的にオフになります。

Pogoピンを使用する場合の開発者向けのドキュメントがこちらにあります。


UnityのVRサポートのみを使用する場合

SteamVRプラグインを使用せず、Unity本体のVRサポート機能のみでHTC Viveを使用することもできます。ただし、シャペロン境界が表示されない、Viveコントローラーのグラフィックスが表示できないといった制限があります。基本的にはSteamVRプラグインの使用がおすすめですが、以下で使用しない方法を説明します。 

プロジェクトを作成したら、Edit > Project Settings > PlayerでPlayer Settingsを開きます。Virtual Reality Supportedをチェックして、下に出てきたVirtual Reality SDKsの右下の+ボタンを押して、OpenVRを追加してください。

再生するか、ビルドして実行するとヘッドセットで見ることができます。ミラー画面が正常に表示されない場合は、起動ダイアログで非フルスクリーンにしたり、表示するディスプレイを変えてみてください。

位置トラッキングのモードを切り替えるには

Unity本体のVRサポートのAPIで位置トラッキングのモードを変更できます。ルームスケールモード(TrackingSpaceType.RoomScale)と着席モード(TrackingSpaceType.Stationary)が選択でき、Viveではデフォルトはルームスケールになっています。

using UnityEngine;
using UnityEngine.XR;

public class Recenter : MonoBehaviour
{
    void Start()
    {
        // デフォルトはTrackingSpaceType.RoomScale
        VRDevice.SetTrackingSpaceType(TrackingSpaceType.Stationary);
    }

    void Update()
    {
        // Rキーで位置トラッキングをリセットする(Stationaryでしか動作しない)
        if (Input.GetKeyDown(KeyCode.R))
        {
            InputTracking.Recenter();
        }
    }
}

Viveコントローラーの位置・向きを取るには

InputTracking.GetLocalPosition/GetLocalRotationでVRNode.LeftHandまたはVRNode.RightHandを指定すると、Viveコントローラーの位置と向きを取得できます。サンプルコードです。

using UnityEngine;
using UnityEngine.XR;

public class ViveControllerTest : MonoBehaviour
{
    void Update()
    {
        transform.localPosition = InputTracking.GetLocalPosition(VRNode.LeftHand);
        transform.localRotation = InputTracking.GetLocalRotation(VRNode.LeftHand);
    }
}

トリガーやボタンの入力を取るには

ボタンやトリガーの状態をジョイスティック入力として取得できます。Input.GetJoystickNames()のジョイスティック名で「OpenVR Controller - Left」「OpenVR Controller - Right」がそれぞれ返ってきます。

軸とボタンのマッピングは以下のようになっています。

Axis/Button
3th axis トリガー(離す0~押し込む1)
4th axis タッチパッド(左-1~右1)
5th axis タッチパッド(下-1~上1)
10th axis トリガー(離す0~押し込む1)
12th axis グリップボタン(離す0、押し込む1)
joystick button 0 アプリケーションメニュー
joystick button 9 タッチパッドをクリック
joystick button 15 トリガー(軸のトリガーが0.3あたりでTrueになる)
joystick button 17 タッチパッドに触れている

トリガーをクリック直前まで引くと0.83前後まで上がり、カチッとクリックすると1にジャンプするようです。


その他

イメージエフェクトを使うには

SteamVRプラグインでは、[CameraRig]の中のCamera (eye)にイメージエフェクトのスクリプトをアタッチしてください。

Unityの新しいPost Processing Stackのスクリプト(Post Processing Behaviour)も使用できます。AntialiasingのTemporal Anti-aliasing、Screen Space Reflection、Motion BlurはVRでは動作しないので注意です。

PCの画面に別視点の映像を表示するには?

カメラをシーンに追加し、Target Eyeを「None (Main Display)」に設定してください。また、余分なAudio Listenerを削除してください。実行すると別視点の画面がPC側に表示されます。カメラのdepth値に注意してください。なお当然ですが、一画面余分にレンダリングしますので、そのぶん負荷が増えます。

別のPCでプログラムをもう1つ動かして、ネットゲームの要領で同期表示することを検討してもいいかもしれません。

シーンを切り替えるときに白い部屋が表示される

SceneManager.LoadSceneでシーンを普通に変更すると、HTC Viveの画面が一瞬アプリを起動していないときのデフォルト画面になる場合があります。メインのシーンを切り替えない作りにするか、または、SteamVRプラグインにSteamVR_LoadLevel.csというサポートスクリプトが入っていますので、こちらを使用してみてください。

SteamVR_LoadLevel.Begin(”[シーン名]“)でシーンを切り替えられるほか、いくつかの機能があるようです。

エディタ上で再生するとカクつくんだけど

Gameビューの「Maximize on Play」をオンにして再生してみてください。最大化せずに再生すると、インスペクタの表示などが負荷になり、フレームレートが低下します。

レンダリング解像度を変更するには?

GPUの描画負荷は、描画するピクセル数と、ピクセルあたりのシェーダの複雑さに比例することが多いです。パフォーマンスが出ない場合、VRSettings.renderScaleでレンダリング解像度を下げると効果がある場合があります。

UnityEngine.VR.VRSettings.renderScale = 0.5f; // デフォルトは1.0f

VR空間でシーンを編集するには

UnityからEditorVRというVR空間で直接シーンを編集できる実験ビルドが提供されています。「Unity EditorVRについて」ページを参照してください。

その他のTips

SteamVRの設定>パフォーマンス>「フレームタイミングを表示」で、フレームごとのCPU・GPUの処理時間のグラフを表示できます。解説がこちらのページにあります。


参考リンク

書いた人:こりん(@k0rin
リンク切れや間違いなどございましたらメールやTwitterでご指摘ください。
VR開発メモトップ RSS