VR開発メモトップ

Unity+HTC Vive開発メモ

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

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

更新履歴

(2017年1月14日)SteamVRプラグインのトラッキングオブジェクト数について追記
(2017年1月12日)最新のSteamVRプラグインがUnity 5.6.0b3で動作することを確認
(2017年1月9日)記述の整理、Unity 5.6.0b3で引き続きSteam VRプラグインが動作しないことを確認
(2016年12月20日)「VR空間でシーンを編集するには」を追加
(2016年12月17日)Unity 5.6.0b1でSteam VRプラグインが動かないのを注記

目次


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

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

なお、最新のSteam VRプラグイン(バージョン1.2.0)を使用すると、ベータ版プログラムのUnity 5.6でも動作するようです。

プラグインをインポートすると、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 RKeyToRecenter : 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オブジェクトまでトラッキングできるようです。


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

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

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

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

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

位置トラッキングはルームスケールのモードになります。着席モードにしたい場合はSteamVRプラグインを使用することになります。プラグインを使いたくない場合は、SteamVR/Plugins/openvr_api.csファイルだけをプロジェクトにコピーして、以下のようなコードで着席モードにすることも一応できます(Valveの開発者のコメント)。

using UnityEngine;
using Valve.VR;

public class OpenVRRecenter : MonoBehaviour
{
    void Start()
    {
        // 着席モードにする
        OpenVR.Compositor.SetTrackingSpace(ETrackingUniverseOrigin.TrackingUniverseSeated);
    }

    void Update()
    {
        // Rキーで位置トラッキングをリセットする
        if (Input.GetKeyDown(KeyCode.R)) {
            OpenVR.System.ResetSeatedZeroPose();
        }
    }
}

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

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

using UnityEngine;
using UnityEngine.VR;

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

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

Unity 5.5より、ボタンやトリガーの状態をジョイスティック入力として取得できます。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側に表示されます。なお当然ですが、一画面余分にレンダリングしますので、そのぶん負荷が増えます。

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

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

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

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

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

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

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

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

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

Failed to find Sprites-Default.matというエラーが出る

エディタ上で以下のようなエラーが出る場合、

Failed to find Sprites-Default.mat
UnityEditor.AssetDatabase:GetBuiltinExtraResource(String)
SteamVR_PlayArea:BuildMesh() (at Assets/SteamVR/Scripts/SteamVR_PlayArea.cs:172)
SteamVR_PlayArea:Update() (at Assets/SteamVR/Scripts/SteamVR_PlayArea.cs:210)

SteamVRプラグインが古いのが原因です。version 1.1.1以降に更新してください。

ビルドして実行するとクラッシュする

Unity 5.5.0b3現在、Player SettingsのGraphics APIs for WindowsでDirect3D12 (Experimental)が選択されているとクラッシュするようです。Direct3D11を追加して選択されるようにしてください。

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

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

その他のTips

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


参考リンク

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