Unity + Oculus Quest 2 開発メモ

最終更新日:2021年07月23日

UnityでOculus Quest対応アプリを開発する方法やTipsをまとめています。

更新履歴

(2021年7月23日)「Unity VR開発メモ(XR Interaction Toolkit)」へのリンクを追加
(2021年7月8日)XR Plug-in Managementの有効化を簡単な方法に変更、OVRCameraRigの記述を現在の動作にあわせて修正、ハンドトラッキングの不具合修正について追加、「Quest 2 でアプリから外部ストレージにアクセスできない」を追加、その他
(2021年7月1日)「Unity の Oculus Quest 対応の概要」にUnity 2020.3.13以降のASTC圧縮の高速化について追記
(2021年6月23日)「Oculus Browser でデベロッパーツールを使用するには」を追加
(2021年6月14日)「App Lab」の項目に「VR刺身タンポポ」をリリースして得た経験をいくらか反映しました。


Unity で Oculus Quest 対応アプリを作るには

Unity の Oculus Quest 対応の概要

UnityはOculus Quest / Oculus Quest 2に対応しています。Oculus QuestはAndroidベースですので、Androidアプリとしてビルドします。

Unityの標準機能だけでもOculus Questで動くVRアプリはビルドできますが、Oculus Quest固有の機能の多くを利用したり、Oculus Store(App Lab)でアプリをリリースするには、Oculus Integrationを使用することになります。以降本ページではOculus Integrationベースで解説します。

必要なUnityのバージョンは Set Up Development Environmentのページで確認できます。2021年7月現在は2019.4 LTS以降に対応しており、筆者の手元では2021.1および2020.3で安定して開発できています。

Oculus Quest を開発者モードにする

Oculus Quest で自作のアプリが動くようにするには、「開発者モード」にする必要があります。スマートフォンにインストールした Oculus アプリの右下の「設定」をタップし、接続されている Oculus Quest をタップで開いて「その他の設定」をタップすると「開発者モード」が出てきますのでオンにしてください。

Oculus Quest を PC に USB ケーブルで接続すると、Oculus Quest の画面に「USB デバッグを許可しますか?」というダイアログが開きますので許可します。

Oculus Quest アプリをビルドするには

Quest アプリをビルドする手順を示します。

Unity Hub で Unity をインストールする

Unity Hub で Unity をインストールします。インストールするときに、モジュールの追加で Android Build Support のところにすべてチェックを入れてください。

Unity でプロジェクトを作成する

Unity の新規プロジェクトを「3D」テンプレート(Legacy Render Pipeline)または「Universal Render Pipeline」テンプレートで作成し、以下の手順を行います。

  • Build Settings を開いてプラットフォームを Android に、Texture Compression(テクスチャの圧縮方式)を ASTC に変更して Switch Platform する
  • Player Settings で Player > Other Settings > Graphics APIs の Vulkan を削除する(現在 Vulkan が実験的なステータスのため)
  • 必要なら Color Space も Linear に変えておく
  • その下のほうの Minimum API Level を「Android 6.0 ‘Marshmallow’ (API level 23)」に変更する

Oculus Integarion をインポートする

Asset Store にある Oculus Integration をプロジェクトにインポートします。

インポートが完了すると Oculus の新しいプラグインを認識しましたというダイアログメッセージが出るので、「Yes」「Upgrade」「Restart」等をクリックしてください。

XR Plug-in Management を有効化する

以下の手順でUnity の XR サポートを有効にします。

  • Edit > Project Settings > XR Plugin Management を開いて「Install XR Plugin Management」ボタンを押す
  • Plug-in Provides が表示されたら “Oculus” にチェックを入れる(タブを切り替えて Android と Standalone 両方でチェックを入れてください)

上記の操作により、プロジェクトに XR Plugin Management パッケージと Oculus XR Plugin パッケージがインストールされます。

ビルド・実行する

試しに Project ウィンドウの Oculus/VR/Scenes/ControllerModels シーンを開いてみてください。

Build Settings を開き Oculus Quest を接続して Build and Run ボタンを押し、適当な .apk ファイル名を指定するとビルドと実機への転送が始まります。初回ビルド時に多数のシェーダーのビルドが走りますが、2度目以降はキャッシュされるので時間が短くなります。成功するとアプリが起動し、コントローラーで操作ができます。

ビルドしても動かないときは

Quest の画面が真っ暗のまま動かないときは、Unity の XR サポートがきちんと有効になっていないケースが多いです。

  • Window > Package Manager で XR Plug-in Management パッケージと Oculus XR Plugin パッケージがインストールされているか、バージョンが新しいかを確認してください。
  • Edit > Project Settings で XR Plug-in Management の Oculus と Initialize XR on Startup のチェックが入っているかを確認してください。

Quest へのインストール時にエラーが出るときは、アプリを一度アンインストールすると上手く行く場合があります。

また、参考までに2020年8月の xR Developers Community Conference で Quest のビルド実演をした動画を貼っておきます(基本的な手順は一緒ですが、バージョン等が古いので注意してください。特に現在では Unity 2020 や 2021 でも大丈夫です)。

Build and Run したアプリを再度実行するには

ビルドして実機に転送したアプリは、Quest に格納されていていつでも実行できます。「アプリ(Apps)」メニューの右上の「すべて」を「提供元不明(Unknown Sources)」に変更したところに入っています。

右端の「︙」ボタンからアンインストールもできます。

Unity のエディタで再生するには

Oculus Integration は PC ヘッドセットにも対応しています。Quest を Oculus Link で接続すると PC ヘッドセットとして認識され、Unity のエディタ上で再生できます。ビルドしなくても実行できるため開発が楽になります。

Rift S も使用できます。Rift S だとバッテリーや USB 接続の安定性を気にしないでいいメリットが、Quest だとハンドトラッキングも使用できるメリットがあります。「Oculus Link でハンドトラッキングを使用するには」を参照してください。

Quest のログ出力を見るには

Unity の Android Logcat パッケージをインストールしておくと便利です。

Android のログ出力(adb logcat コマンド)を Unity のウィンドウで簡単に閲覧できます。Window > Package Manager からインストールします。Build and Run すると自動的にデバイスに接続してログが表示されます。キーワードでフィルタをかけたり、スクリーンショットを撮ることもできます。

なお Quest から Android Logcat に大量のログが出力されている場合、Unity が非常に重くなることがあるので注意してください。

また、Android Logcat 1.2.0 以降で Memory Window を表示していると定期的にフレーム落ちするようなので無効にしておいたほうがいいでしょう(右上の Tools > Memory Window > Disabled)。

Oculus Integration をアップデートするには

Oculus Integration のバージョンを上げるときは以下の手順で行えばだいたい大丈夫です。

  • Unity をいったん終了する(プラグインの DLL を解放するために必須です)
  • Assets/Oculus フォルダを削除
  • Unity を起動しプロジェクトを開いて Oculus Integration を再インポート

こちらのページに公式の説明があります。

Oculus Developer Hub について

Oculus Developer Hub という開発用のポータルツールが提供されています。ヘッドセット内の映像の中継表示や、ガーディアンや近接センサーの無効化、アプリの起動やアンインストール、Oculus が提供するツールのダウンロード等の機能がありますので、インストールしておくと便利です。


プロジェクトの基本設定について

グラフィックス設定

レンダリング・パイプラインはレガシーと Universal Render Pipeline のどちらでも大丈夫です。リニアカラーにも対応しています。フォワードレンダリングにするのが基本になります。

アンチエイリアスは Project Settings > Quality > Anti Aliasing で設定します。2x はとても軽量でかつ効果が大きいので最低でも必ずかけておいたほうがいいです。4x はポリゴンのエッジがさらにすっきりしますが若干重めです。重いシェーダーで画面の広範囲が覆われたときに負荷増が顕著になります。8x は 4x と違いが分からず、4x までしか効かない感じがします。

その他、

  • Project Settings > Oculus の Stereo Rendering Mode を Multiview にする(Standalone のほうは Single Pass Instanced にする)
  • Dynamic Batching と Static Batching をオンにする(Graphics Jobs は使わないこと)

が基本設定になると思います。

関連項目として「描画解像度について」も参照してください。

カメラの配置

Oculus Integration を使用する場合、シーンのカメラとしてデフォルトの Main Camera の代わりに Oculus/VR/Prefabs/OVRCameraRig プレハブをシーンに配置します。プレハブにアタッチされている OVRCameraRig.cs によって、OVRCameraRig の孫の CenterEyeAnchor オブジェクトがヘッドセットに追従します。

OVRCameraRig は、シーン内のプレイヤーの視点の位置(正確には両目の中間)に配置します。その上で、実行中にちょうどいい場所に立つ、椅子に深く座るなどしてコントローラーの Oculus ボタン長押しでリセンターすることで位置合わせができます。

また、立って体験するソフトを作る場合は、OVRCameraRig オブジェクトをシーンの地面と同じ高さに配置して、OVR Manager コンポーネントの Tracking Origin Type を Floor Level に変更します。

ビルド設定

Oculus Store にアプリを提出する場合は Project Settings で Scripting Backend を IL2CPP にして ARM64 でビルドする必要があります。が、IL2CPP はビルドが重いので、開発中や、ストアにリリースするつもりのない制作物では Mono でいいかもしれません。

adb コマンドについて

adb (Android Debug Bridge) は Android デバイスの各種操作を行うためのコマンドラインツールです。Quest でも使用できます。

adb コマンドは下記ページからダウンロードできるほか、Unity のインストール時に同梱されるものを使用することもできます。Unity の Android Logcat パッケージをインストールして、Android Logcat のウィンドウの右上の Open Terminal をクリックすると Platform-Tools のインストールフォルダが開きます。

よく使うコマンドをいくつか紹介します。

Quest の接続を確認するには

$ adb devices

アプリをアンインストールするには

$ adb uninstall [パッケージ名]

パッケージ名は、Quest のアプリ > 提供元不明 で確認したり、下記コマンドで一覧表示できます。

インストールされているアプリの一覧を表示するには

$ adb shell cmd package list packages

apk ファイルをインストールするには

$ adb install [apk ファイル]

アプリを実行するには

下記コマンドで Unity でビルドした指定パッケージ名のアプリを実行できます。

$ adb shell am start -n [パッケージ名]/com.unity3d.player.UnityPlayerActivity

Quest の IP アドレスを確認するには

$ adb shell ifconfig

モーションコントローラーについて

Quest は Touch というモーションコントローラーを使用します。

Touch コントローラーは、主に外周リング部に埋め込まれた赤外線 LED を Quest 本体の赤外線カメラで位置トラッキングする仕組みになっています。バッテリーは左右それぞれ単三電池一本です。エネループ等も使用できます。連続使用で3時間から4時間ほど動作します(Quest 2 では稼働時間が伸びているようです)。

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

Oculus/VR/Prefabs フォルダにある OVRCameraRig をシーンにドロップし再生すると、OVRCameraRig の孫の LeftHandAnchor、RightHandAnchor が Touch の位置になります。

また、OVRInput.GetLocalControllerPosition / GetLocalControllerRotation で位置を取得することもできます。

コントローラーを VR 空間に表示するには

OVRCameraRig の LeftHandAnchor および RightHandAnchor に OVRControllerPrefab をアタッチして、右手の OVRControllerPrefab の Controller を R Touch に変更してください。

Oculus/VR/Scenes/ControllerModels シーンに上記セットアップ済みの OVRCameraRig があるので参考になります。

コントローラーの操作で動く手を表示するには

OVRCameraRig の LeftHandAnchor に CustomHandLeft を、RightHandAnchor に CustomHandRight をアタッチすると、コントローラーの操作で動く手を表示できます。

Oculus/SampleFrameworks/Usage/CustomHands シーンに上記セットアップ済みの OVRCameraRig があるので参考になります。

トリガーやボタンの状態を取得するには

OVRInput クラスで Touch のボタン・スティック・トリガーの状態を取得できます。

OVRInput は異なる種類のコントローラーの入力を抽象化して取得できるようになっていますが、ややこしいので、とにかく Touch の入力を一通り読む方法をコードの形で示します。正確なところは OVRInput のドキュメントページを参照してください。

ボタン・トリガーを押したか調べるには

if (OVRInput.GetDown(OVRInput.RawButton.A))
{
    Debug.Log("Aボタンを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.B))
{
    Debug.Log("Bボタンを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.X))
{
    Debug.Log("Xボタンを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.Y))
{
    Debug.Log("Yボタンを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.Start))
{
    Debug.Log("左手メニューボタンを押した(オン・オフ不安定なので注意)");
}

if (OVRInput.GetDown(OVRInput.RawButton.RIndexTrigger))
{
    Debug.Log("右人差し指トリガーを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.RHandTrigger))
{
    Debug.Log("右中指グリップを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.LIndexTrigger))
{
    Debug.Log("左人差し指トリガーを押した");
}
if (OVRInput.GetDown(OVRInput.RawButton.LHandTrigger))
{
    Debug.Log("左中指グリップを押した");
}

GetDown ではなく Get を使うと押しっぱなしの状態で true、GetUp でリリースの瞬間に true になります。

なお、右手の Oculus ボタンの状態は取得できません。

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

// 右人差し指トリガー
float rTrigger1 = OVRInput.Get(OVRInput.RawAxis1D.RIndexTrigger);
// 右中指グリップ
float rTrigger2 = OVRInput.Get(OVRInput.RawAxis1D.RHandTrigger);
// 左人差し指トリガー
float lTrigger1 = OVRInput.Get(OVRInput.RawAxis1D.LIndexTrigger);
// 左中指グリップ
float lTrigger2 = OVRInput.Get(OVRInput.RawAxis1D.LHandTrigger);

離すと 0.0、押し込むと 1.0 になります。ただし、完全に押し込んでも 1.0 まで上がらなかったり、すぐに 1.0 にならなかったりしますので、押し込んだかどうか判定する場合には閾値を設ける必要があります。

選択、キャンセル入力を取得するには

Button.One、Two を使用すると便利です。それぞれ Touch の A/X ボタン、B/Y ボタンにマップされていますが、セットアップ時の説明にも表示されるスタンダードな操作となっています。

if (OVRInput.GetDown(OVRInput.Button.One))
{
    Debug.Log("選択した");
}
if (OVRInput.GetDown(OVRInput.Button.Two))
{
    Debug.Log("キャンセルした");
}

アナログスティックの入力を取得するには

下記で取得できます。Vector2 の X 軸が左 -1.0 ~右 1.0、Y 軸が下 -1.0 ~上 1.0 になっています。

// 左手のアナログスティックの向きを取得
Vector2 stickL = OVRInput.Get(OVRInput.RawAxis2D.LThumbstick);
// 右手のアナログスティックの向きを取得
Vector2 stickR = OVRInput.Get(OVRInput.RawAxis2D.RThumbstick);

アナログスティックをボタンとして押し込むこともできます。

if (OVRInput.GetDown(OVRInput.RawButton.LThumbstick))
{
    Debug.Log("左アナログスティックを押し込んだ");
}
if (OVRInput.GetDown(OVRInput.RawButton.RThumbstick))
{
    Debug.Log("右アナログスティックを押し込んだ");
}

また、デジタル4方向の入力も取得できます。メニュー選択等に便利です。

if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickUp))
{
    Debug.Log("左アナログスティックを上に倒した");
}
if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickDown))
{
    Debug.Log("左アナログスティックを下に倒した");
}
if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickLeft))
{
    Debug.Log("左アナログスティックを左に倒した");
}
if (OVRInput.GetDown(OVRInput.RawButton.LThumbstickRight))
{
    Debug.Log("左アナログスティックを右に倒した");
}

if (OVRInput.GetDown(OVRInput.RawButton.RThumbstickUp))
{
    Debug.Log("右アナログスティックを上に倒した");
}
if (OVRInput.GetDown(OVRInput.RawButton.RThumbstickDown))
{
    Debug.Log("右アナログスティックを下に倒した");
}
if (OVRInput.GetDown(OVRInput.RawButton.RThumbstickLeft))
{
    Debug.Log("右アナログスティックを左に倒した");
}
if (OVRInput.GetDown(OVRInput.RawButton.RThumbstickRight))
{
    Debug.Log("右アナログスティックを右に倒した");
}

ボタン・スティック・トリガーに触れているか調べるには

中指グリップとメニューボタン、Oculus ボタン以外について、指が触れているかどうかを調べることができます。

if (OVRInput.Get(OVRInput.RawTouch.LIndexTrigger))
{
    Debug.Log("左人差し指用トリガーに触れている");
}
if (OVRInput.Get(OVRInput.RawTouch.LThumbstick))
{
    Debug.Log("左アナログスティックに触れている");
}

if (OVRInput.Get(OVRInput.RawTouch.RIndexTrigger))
{
    Debug.Log("右人差し指用トリガーに触れている");
}
if (OVRInput.Get(OVRInput.RawTouch.RThumbstick))
{
    Debug.Log("右アナログスティックに触れている");
}

if (OVRInput.Get(OVRInput.RawTouch.A))
{
    Debug.Log("Aボタンに触れている");
}
if (OVRInput.Get(OVRInput.RawTouch.B))
{
    Debug.Log("Bボタンに触れている");
}
if (OVRInput.Get(OVRInput.RawTouch.X))
{
    Debug.Log("Xボタンに触れている");
}
if (OVRInput.Get(OVRInput.RawTouch.Y))
{
    Debug.Log("Yボタンに触れている");
}

近接センサーの入力を調べるには

人差し指用トリガーとアナログスティックには近接センサーがあり、指が近づいた(ちょっと浮いている)状態を取得できるようになっています。

if (OVRInput.Get(OVRInput.RawNearTouch.LIndexTrigger))
{
    Debug.Log("左人差し指用トリガーの近くに指がある");
}
if (OVRInput.Get(OVRInput.RawNearTouch.LThumbButtons))
{
    Debug.Log("左アナログスティックの近くに指がある");
}

if (OVRInput.Get(OVRInput.RawNearTouch.RIndexTrigger))
{
    Debug.Log("右人差し指用トリガーの近くに指がある");
}
if (OVRInput.Get(OVRInput.RawNearTouch.RThumbButtons))
{
    Debug.Log("右アナログスティックの近くに指がある");
}

この近接センサーを使用して、指をさしている状態を取得できます。人差し指を伸ばすと RawNearTouch.RIndexTrigger が false になります。

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

OVRInput.SetControllerVibration を使用します。周波数と振動の大きさをそれぞれ 0~1 で指定します。両方とも0に設定すると振動が止まります。振動をオンにすると振動を続け、2秒後に止まるようになっています。多くの場合は自分で明示的に0にして止める必要があります。

周波数については変えても体感でほとんど分からないのですが、1よりも0のほうが心持ち荒い振動のような感じがします。

右手トリガーを引いている間振動を続けるサンプルスクリプトです。適当な GameObject にアタッチしてください。

using UnityEngine;

public class ControllerVibrationTest : MonoBehaviour
{
    void Update()
    {
        // 右手トリガーを引いたら振動を開始
        if (OVRInput.GetDown(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch))
        {
            OVRInput.SetControllerVibration(0f, 1f, OVRInput.Controller.RTouch);
        }
        // 右手トリガーを離したら振動を停止
        if (OVRInput.GetUp(OVRInput.Button.PrimaryIndexTrigger, OVRInput.Controller.RTouch))
        {
            OVRInput.SetControllerVibration(0f, 0f, OVRInput.Controller.RTouch);
        }
    }
}

ハンドトラッキングについて

ハンドトラッキングを使用するには

まず Quest の「設定 > デバイス > ジェスチャーコントロールとコントローラー」の「ジェスチャーコントロール」をオンにしてハンドトラッキングを有効にしてください。

Oculus/VR/HandTest シーンがハンドトラッキングの最小限のサンプルシーンなので、こちらを動かしてみるのが手っ取り早いです。シーンを開いたら OVR Manager の Hand Tracking Support を Controllers And Hands に変更してビルドしてみてください。

OVRCameraRig の LeftHandAnchor / RightHandAnchor に OVRHandPrefab と OVRControllerPrefab がアタッチされていて、現在の入力に応じて手またはコントローラーが適宜表示されるようになっています。

ハンドトラッキングの精度を上げるには

Oculus Integration v28 より、Oculus Quest 2 では高頻度でハンドトラッキングができるようになりました。OVR Manager の Hand Tracking Frequency で設定できます。

HIGH にするとデフォルトの 30Hz ではなく 60Hz でトラッキングを行い、認識がよくなり低遅延になります。計算負荷によるオーバーヒートを避けるため、Hand Tracking Frequency が LOW のときは CPU レベル3・GPU レベル3、HIGH のときは CPU レベル3・GPU レベル2 までに制限されます。HIGH のとき GPU レベル2 までなのは結構厳しいので注意が必要です。「CPU・GPU レベルについて」を参照してください。

Oculus Link を使用して、Unity のエディタ上でハンドトラッキングを使用できます。ビルドしなくても動作をテストできるため、ハンドトラッキングを使用したアプリの開発が楽になります(逆にビルドした実行ファイルでは使えないため要注意です)。

「設定 > デバイス > ジェスチャーコントロールとコントローラー」の「手とコントローラーの自動切り替え」をオンにした状態で Oculus Link を有効にしてください。このオプションがオフだと、ハンドトラッキングをオンにしていても Oculus Link を接続した時点でオフに戻ってしまうのでエディタで手が使用できないようです。

手のマテリアルを変更するには

OVRHandPrefab を使用している場合、Skinnded Mesh Renderer に BasicHandMaterialがアサインされているので、これを変更することで手のマテリアル・テクスチャを変更できます。

指でつまむ動作を取得するには

左右の OVRHandPrefab にアタッチされている OVRHand に対して GetFingerIsPinching(finger) を呼ぶと、それぞれの指先がほかのいずれかの指先とくっついているかどうかを true / false で取得できます。

finger の値
親指 OVRHand.HandFinger.Thumb
人差し指 OVRHand.HandFinger.Index
中指 OVRHand.HandFinger.Middle
薬指 OVRHand.HandFinger.Ring
小指 OVRHand.HandFinger.Pinky

たとえば、親指と人差し指でつまむ形をしているかどうかは下記で判定できます。

public class FingerPinchTest : MonoBehaviour
{
    [SerializeField]
    OVRHand ovrHand;

    void Update()
    {
        if (ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Index) &&
            ovrHand.GetFingerIsPinching(OVRHand.HandFinger.Thumb))
        {
            Debug.Log("つまんでる!");
        }
    }
}

親指と人差し指、親指と中指あたりでつまむ動作は比較的正しく取得できますが、3本以上になるとだいぶ怪しくなります。また、指先と指先がくっついているかの判定であって、指が立っているかどうかを判定するわけではないことに注意してください(たとえばグー・チョキ・パーの認識には使用できない)。

また、指先同士の接触の度合いを GetFingerPinchStrength で取得できます。指先と指先が離れていると0で、近づけると接触する数cmほど前から数値が上がっていき、接触する直前で1になります。

現在ハンドトラッキングかどうかを調べるには

OVRInput.IsControllerConnected を使用すると、現在 Touch コントローラーを使用しているか、ハンドトラッキングを使用しているかを調べることができます。

using UnityEngine;

public class ControllerChecker : MonoBehaviour
{
    void Update()
    {
        var usingHand = OVRInput.IsControllerConnected(OVRInput.Controller.Hands);
        var usingTouch = OVRInput.IsControllerConnected(OVRInput.Controller.Touch);

        Debug.Log($"usingHand = {usingHand} / usingTouch = {usingTouch}");
    }
}

なお、OVRInput.Controller.Hands と OVRInput.Controller.Touch は切り替わりの瞬間に両方とも false になっている場合があります。

キーボードで文字を入力するには

アプリ内で Quest のキーボードを呼び出して文字入力に使用できます。

Project ウィンドウの Oculus/OculusProjectConfig で Focus Aware と Require System Keyboard を有効にして、TouchScreenKeyboard.Open でキーボードが開きます(Quest 実機のみ)。TouchScreenKeyboard.text で入力文字を取得できます。

また、Unity UI(uGUI) で Input Field をアクティブにしてもシステムキーボードが表示されます。

Oculus ボタンを押したときの処理

アプリ実行中に右手コントローラーの Oculus ボタンを押すとシステム UI がオーバーレイ表示されますが、このときにアプリを一時停止する、コントローラーの表示を消す等適切な処理を行う必要があります(VRC.Quest.Input.4)。

あらかじめ Project ウィンドウの Oculus/OculusProjectConfig で Focus Aware にチェックを入れてください。Oculus ボタンを押すと OVRManager.InputFocusLost イベントが、復帰すると OVRManager.InputFocusAcquired イベントがトリガーされます。

ポーズ処理の基本としては、Time.timeScale をゼロにするのが簡単です。Oculus/SampleFramework に入っている PauseOnInputLoss.cs が参考になります。

ヘッドセットをはずした・スリープした等を検知するには

ヘッドセットをはずすと OVRManager.HMDUnmounted イベントがトリガーされます。また、Quest 本体のスリープと復帰で MonoBehaviour.OnApplicationPause が呼び出されます。

その他、Quest のアプリケーションライフサイクルで送られてくるイベントの一覧が下記ページにあります(InputFocusLost についてはまだ記載がないようです)。

Oculus Quest 2 かどうかを判定するには

OVRManager.systemHeadsetType で使用しているヘッドセットの種類が取得できます。SystemHeadsetType 列挙型で Oculus_Quest または Oculus_Quest_2 が返ってきます(Quest 3 以降のことを考えると「Quest 1 ではない」ことを判定したほうがよさそうです)。

var isQuest1 = OVRManager.systemHeadsetType == OVRManager.SystemHeadsetType.Oculus_Quest;
if (!isQuest1)
{
    // Quest 2 以降での処理
}

Oculus Link で接続している場合は Oculus_Link_Quest が返ってきます。Oculus Integration 23.0 では Quest 2 の場合 Oculus_Link_Quest_2 になるようです。

ビルドを速くするには

IL2CPP ビルドをできるだけ避ける

Oculus Store で リリースするためには IL2CPP/ARM64 でビルドする必要がありますが、開発中は Mono/ARMv7 でビルドするようにするととても楽です。

USB 3.0 ケーブルを使う

USB 3.0 ケーブルを使うとビルドした apk ファイルの Quest への転送時間が短くなります。下記エントリが参考になります。

Skip Unneeded Shaders

Unity のモバイルビルドではグラフィックス性能によって Tier 1 から Tier 3 までの描画設定が切り替わるようになっていますが(Project Settings > Graphics を参照)、Quest では Tier 2 しか使用されません。Tier 2 で使用されるシェーダーのみをビルドする設定を有効にするとビルド時間が短くなります。

Oculus > Tools > Oculus Platform Tool でウィンドウを開くと Project ウィンドウに Oculus/OculusProjectConfig が生成されますので、選択して Skip Unneeded Shaders をチェックしてください。

シェーダーストリッピングの処理は OVRShaderBuildProcessor.cs で行われています。

OVR Scene Quick Preview

Oculus Integraion に含まれているツールで、Quest でアプリを起動したままでシーンを更新できるツールです(Asset Bundle を使用しています)。簡単な実験シーンを更新して試したり、負荷を比較したりするのに使用できます。

使用方法は以下の通りです。

  • あらかじめ Build Settings でシーンを登録しておく
  • Oculus > OVR Build > OVR Scene Quick Preview メニューでウィンドウを開く
  • 「Build and Deploy App」で転送用のアプリをビルド・起動する
  • 「Build and Deploy Scene(s)」を押すと Quest のシーンが更新される

OVR Build APK and Run

Oculus > OVR Build > OVR Build APK and Run でビルドすると、ビルドの最終段の “Building Gradle project” がキャッシュされ、ビルド時間が数割短縮されます。ショートカットキーが割り当てられており、Ctrl+K で実行できます。

このメニューは Unity のプラットフォームを Android にしないと表示されないので注意してください。また、Android Logcatを使用している場合は、実行開始時にフィルターが自動で設定されないため自分で設定する必要があります。

パフォーマンス最適化

フレームレートの維持の必要性

Oculus Store および App Lab でリリースする Quest アプリはガイドラインにより、黒画面になっているときやシーンの読み込み中を除き 72 fps で 45 分間連続動作することが求められます(VRC.Quest.Performance.1)。App Lab では 60Hz に条件が緩和されているようです。

72 fps 動作するためには、パイプライン動作する CPU と GPU の1フレームあたりの処理がどちらも 13.888… ミリ秒以内に収まっている必要があります。Quest は性能の高くないモバイル機ですので、最適化の方法についてよく知っておく必要があります。

Quest の CPU・GPU について

Oculus のブログの下記エントリに Quest のハードウェア詳細の解説があります。

Quest は Snapdragon 835 を搭載しています。CPU は 8 コアで、4 つが性能重視の Performance コア(Cortex-A73)、4 つが省電力重視の Efficiency コア(Cortex-A53)です。アプリの実行に割り当てられているのは 3 つの Performance コアで、残りの 1 つの Performance コアはタイムワープとシステムサービスに、4 つの Efficiency コアはトラッキングやその他のシステムソフトウェアに使用されているとのことです。

GPU は Snapdragon 835 に統合されている Adreno 540 です。多くのモバイル GPU 同様タイルベースレンダリングであることに注意が必要です。

CPU・GPU レベルについて

Quest の CPU・GPU は、バッテリー消費を抑えつつフレームレートを維持するために、CPU・GPU レベルという形でクロック周波数が変動します。現在のレベル・クロック周波数は OVR Metrics Tool を使うとヘッドセット内で確認できます。

最低レベルを下記 API で設定できます。たとえば 2 に設定すると、通常時がレベル 2 で、負荷が増えて処理オーバーしそうになるとレベル 3、4 と上がっていき、処理に余裕ができるとゆっくりと 2 に戻っていきます。

OVRManager.cpuLevel = 2;
OVRManager.gpuLevel = 2;

できるだけ低いレベルでアプリが 72 fps 動作するようにしておくのが好ましいですが、低いレベルに設定していると突然負荷がかかったときにフレーム落ちする場合があります。このあと負荷がかかる処理が来ると分かっているようなときに、あらかじめレベルを上げておくことができます。

注意が必要なのがパフォーマンス評価のときで、レベルによって CPU・GPU の使用率の数値が変動します。使用率を比較する場合はレベルを 4(最大)に設定して固定しておいたほうがいいでしょう。

パフォーマンス最適化に使用できるツール

Quest のパフォーマンス最適化に使用できるツールは Oculus、Unity、Google、Qualcomm から提供されているものやオープンソースのツール等多岐に渡ります。どれを使えばいいか迷ってしまいますが、うち特に効果的なものをいくつか紹介します。

OVR Metrics Tool

真っ先に使用するべきツールがこれになります。フレームレートや Quest の各種メトリクスをヘッドセット内でオーバーレイ表示できます。アプリがどんなシチュエーションで重くなるかを簡易的に確認できます。

下記ページから apk ファイルをダウンロードして adb install でインストール、Quest で起動すると設定画面が出てきますので、「Enable Persistent Overlay」を有効にすると常時表示された状態になります(表示されなくなったときは電源ボタンで一度スリープして復帰してみてください)。

まずは設定画面の真ん中にあるプリセットの「BASIC」ボタンあたりをクリックしてください。また、デフォルトでは表示位置が上すぎると思いますので、Pitch を調整してみてください。

STATS タブで表示項目をひとつひとつオン・オフできます。多数の項目がありますが、特に表示しておきたい項目を下記に列挙します。

項目 表示 説明
Average FPS FPS 平均フレームレート
CPU Level CPU L 現在の CPU レベル(0-4)
GPU Level GPU L 現在の GPU レベル(0-4)
GPU Utilization GPU U GPU 使用率(0-100)
Stale Frame Count STALE フレーム落ちした数
Foveation Level FOV 現在の Foveated Rendering のレベル(0-4)

CPU 使用率について、STATS の下のほうにある「CPU Utilization (Core 0-7)」を有効にするとコアごとの数値を表示できます。Core 0-3 が Performance コア、4-7 が Efficiency コアのようです。

また、adb shell ovrgpuprofiler -e を実行すると GPU の細かい項目も有効にできるようになります。無効に戻すには -d オプションです。

OVR Metrics Tool のドキュメントは下記ページにあります。

Unity のプロファイラー

Quest 実機に対して、Unity のプロファイラーを使用して CPU のプロファイリングができます。Build Settings で Development Build を有効にしてビルド・実行して、プロファイラーを開き、プロファイラー上部の Playmode と表示されているところを AndroidPlayer に変更してください。

上の画像のように Timeline 表示にして、Quest の1フレームの CPU の実行処理の詳細を調べられます。画像のように CPU Usage の列が 13.888… ミリ秒で安定して短冊状になっているのが CPU または GPU の処理オーバーが起きていない正常な状態です。

RenderDoc

RenderDoc はオープンソースのフレームデバッガです。Quest 実機で描画コールの実行をトレースしたり、おおまかな実行時間を計測表示できます。Quest 用に機能が追加された RenderDoc for Oculus が Oculus から提供されていて(Windows 専用)、タイルベースレンダリングのタイムラインを表示したり、Quest の各種メトリクスを表示できるようになっています。

Oculus のブログに使い方が詳しいです。描画コールごとの実行時間が計測表示できますが、タイルベースレンダリングなので参考程度にとのことです。

実行時間を確認するときのおおまかな使い方のメモです。

  • プロジェクトの Build Settings の Development Build をオンにして Build And Run
  • RenderDoc for Oculus を起動
  • 左下の Replay Context を Oculus Quest 2 に変更して接続を待つ
  • 真ん中の Launch Application タブの Executable Path で該当アプリのパッケージ名を com.unity3d.player.UnityActivity を選択して OK ボタン
  • 右下の Launch ボタンを押してアプリを起動
  • 右の Capture Frame(s) Immediately ボタンを押すとフレームがキャプチャされる
  • 保存されたフレームをダブルクリックすると描画コールの一覧が出てくる
  • Event Browser の時計のアイコンをクリックすると描画コールごとのおよその実行時間が出てくる

Snapdragon Profiler

Qualcomm の公式ツールです(Qualcomm の開発者アカウント作成が必要)。Snapdragon の各種メトリクスを詳細表示できます。Snapdragon の CPU 各コアごとの使用率や、タイルベースレンダリングのタイル数等を確認できます。通常は上記の 3 つのツールで十分なので、使用する機会は少ないかもしれません。

その他の参考になる情報

コアな情報として、Oculus のブログの下記エントリに Quest のレンダリングパイプラインの解説があります。

下記エントリに OpenGL と Vulkan でのタイルベースレンダリングの動作についての解説があります。

Qualcomm のサイトの「Adreno OpenGL ES Developer Guide」に Adreno のシェーダー等の最適化の Tips が載っています。

UnityではなくUE4のスライドですが、考え方は一緒ですので参考になります。

描画解像度について

VR ヘッドセットはディスプレイの解像度およびリフレッシュレートが高く、ピクセルの描画が GPU の大きな負荷になります。Standard Shader やユニティちゃんトゥーンシェーダー等、フラグメントシェーダーが重いマテリアルで視野の広範囲が覆われている場合、描画解像度を調整することで、見た目をあまり損なわずに GPU の計算量を下げられる場合があります。

視野全体の解像度の変更

下記 API で視野全体の描画解像度を変更できます。0.9 や 0.8 等に試しに下げてみるのがおすすめです。

using UnityEngine.XR;
...
XRSettings.eyeTextureResolutionScale = 0.5f;

現在の描画解像度は、OVR Metrics Tool の STATS で Eye Buffer Width と Eye Buffer Height の項目を表示すると確認できます。

URP を使用している場合は、Universal Render Pipeline Asset の Render Scale でも変更できます。

周辺視野の解像度の変更

Fixed Foveated Rendering

目立たない周辺視野の描画解像度を下げることによって GPU 負荷を下げる VR 特有の最適化機能です。ケースによりますが、GPU 使用率が 10 %から数十%下がります。

下記 API で使用できます。

// Off、Low、Medium、High、HighTop のいずれかを設定
OVRManager.fixedFoveatedRenderingLevel = OVRManager.FixedFoveatedRenderingLevel.High;

現在のレベルは、OVR Metrics Tool の STATS で Foveation Level の項目をオンにすると確認できます。なお、シーンを開始して1フレーム待たないと設定が反映されない場合がありました。

Foveated Rendering の仕組みとしては、タイルベースレンダリングのタイルごとに解像度を変えているようです。詳細の解説がこちらにあります。

Dynamic Fixed Foveated Rendering

描画負荷によって自動的に Foveated Rendering のレベルを変更させます。かなり使い勝手がいいので描画負荷が大きく変動するコンテンツでおすすめです。通常はオフの状態で、負荷が増えてフレーム落ちしそうになるとレベルが徐々に上がっていき、負荷が減るとまた徐々に下がっていきます。

OVRManager.useDynamicFixedFoveatedRendering = true;

で有効になり、OVRManager.fixedFoveatedRenderingLevel には最大レベルを設定します。

テクスチャを綺麗に表示するには

テクスチャの描画品質を上げたい場合、こちらのエントリが非常に詳しく必読です。

圧縮フォーマットを ASTC にする

Quest のテクスチャ圧縮フォーマットは基本的に ASTC にします。

Build Settings でデフォルトの Texture Compression を ASTC にして、個々のテクスチャで調整が必要な場合は Format で RGB(A) Compressed ASTC の 4x4 block から 12x12 block までを選択します。ブロックサイズを大きくすると圧縮率が上がります(Apply してインスペクター最下部のテクスチャ容量の数値が変わるのを確認してください)。

ミップマップを適切に設定する

VR ではテクスチャに近づいたり離れたりすることが非常に多いです。距離に応じて適切な解像度で描画されるように、テクスチャのインポート設定で Generate Mip Maps をオンにします。

また、距離によってミップマップのポッピング(急な切り替わり)が起きないようにするには Filter Mode を Trilinear に変更します(そこそこ負荷が増えるようです)。

異方性フィルタリングを有効にする

地面や壁など斜めの角度から見ることが多いテクスチャは、インポート設定で Aniso Level を上げると綺麗に表示されます。

リフレッシュレートについて

リフレッシュレートを設定するには

Oculus Quest / Quets 2 アプリのデフォルトのリフレッシュレートは 72Hz ですが、OVRManager.display.displayFrequency で変更できます。

特に Quest 2 はリフレッシュレート 90Hz に対応しています。下記で設定できます。

OVRManager.display.displayFrequency = 90f;

対応しているリフレッシュレートの一覧は OVRManager.display.displayFrequenciesAvailable で取得できます。以下の要領で確認できます。

using System.Linq;
...
if (OVRManager.display.displayFrequenciesAvailable.Contains(90f))
{
    OVRManager.display.displayFrequency = 90f;
}

実際にリフレッシュレートが変更されているかどうかは、Android Logcat または OVR Metrics Tool の表示で確認できます。

リフレッシュレートを取得するには

OVRManager.display.displayFrequency で現在のリフレッシュレートを取得できます。displayFrequency に値を代入して実際にリフレッシュレートが変更されるまでは値が反映されていないため注意してください。

また、Oculus Integration 23.0 以降ではリフレッシュレートが変わると OVRManager.DisplayRefreshRateChanged イベントが飛んできます。

フェードイン・フェードアウトするには

OVRScreenFade を使う

OVRScreenFade を CenterEyeAnchor にアタッチするとシーン開始時にフェードインします。目の前に半透明ポリゴンを描いて覆う仕組みになっています。

フェードアウトするには FadeOut 関数を呼びます。また、Oculus Integration 23.0 からフェードインする FadeIn 関数が追加されています。

OVRManager.SetColorScaleAndOffset を使う

別の方法としては、OVRManager の関数でヘッドセット内の画面全体のピクセルの色にフィルターをかけられます。Quest で効くのは第1引数のカラースケールだけで、カラーオフセットは効かないようです(ただし OpenXR のプラグインを使用している場合はカラーオフセットが使えるようです)。

OVRManager.SetColorScaleAndOffset(Vector4 colorScale, Vector4 colorOffset, bool applyToAllLayers)

DOTween と SetColorScaleAndOffset を使用してフェードイン・フェードアウトするサンプルクラスです。

using DG.Tweening;
using UnityEngine;

public class Fader : MonoBehaviour
{
    Color filterColor;

    void Update()
    {
        if (OVRInput.GetDown(OVRInput.RawButton.A))
        {
            FadeTo(Color.white, 1f);
        }
        if (OVRInput.GetDown(OVRInput.RawButton.B))
        {
            FadeTo(Color.black, 1f);
        }
    }

    public void FadeTo(Color color, float durationSec)
    {
        DOTween.To(() => filterColor, c => filterColor = c, color, durationSec)
            .SetEase(Ease.Linear)
            .OnUpdate(() => OVRManager.SetColorScaleAndOffset(filterColor, Color.clear, true));
    }
}

Oculus Spatializer を使用するには

Oculus Integration には 3D 空間音響のためのプラグインが同梱されています。

Unity のデフォルトのステレオサウンドでは AudioSource を 3D にしても左右の音量バランスが変わるだけで、左右どちらから音が鳴っているかしか分かりませんが、Oculus Spatializer を使用すると、HRTF(頭部伝達関数)を用いて耳に入ってくる音を加工することで、シーン内の音源に立体的な定位感が与えられます。個人差がありますが、前で鳴っている音と後ろで鳴っている音の判別ができたり、上方向・下方向から鳴っている音がそれらしく聴こえるようになります。特に VR においては頭を回すと音源の方向が変わるため、自分が実際にその空間にいるかのような臨場感が増強されます。

また、音が部屋の壁で跳ね返ることによる残響音(初期反射音およびリバーブ)を物理的にシミュレートすることもできるようになっています。

音の定位感を得るには

Audio Source に立体的な定位感を与えるためのセットアップは以下の通りです。

  • Project Settings > Audio で Spatializer Plugin を OculusSpatializer に変更する
  • 同じく DSP Buffer Size を調整して音声の遅延を低減する(Quest では Good latency または Default、PC では Best latency が推奨されています)
  • 音源となる Audio Source がアタッチされているゲームオブジェクトに ONSP Audio Source コンポーネントを追加する

サンプルシーンとして Oculus/Spatializer/scenes/RedBallGreenBall が用意されています(が、このシーンは Spatializer が効いているかどうか分かりにくいかもしれません)。

リバーブについて、ONSP Audio Source をアタッチすると Unity 本体の Audio Reverb Zone は効果がなくなるので注意してください。残響音を得るには次に説明する方法を使うか、Audio Mixer に SFX Reverb を挿す必要があります。

残響音を得るには

  • Project ウィンドウで Audio Mixer を作り、適当な Audio Mixer Group に OculusSpatializerReflection エフェクトを挿して、Audio Source の Output をその Audio Mixer Goup に設定する
  • 上でセットアップした ONSP Audio Source の Reflection Enabled をオンにする

エフェクトの ROOM DIMENTIONS で部屋の大きさを、WALL REFLECTION COEFFICIENTS で壁の反射の度合いを設定します。Enable Reverberation をオンにして SHARED REVERB WET MIX と、ONSP Audio Source の REVERB SEND LEVEL を上げるとリバーブ(音が繰り返し反響する効果)が追加されます。

アプリをリリースするには

Quest のアプリ配布手段について

Oculus Store

Quest の公式アプリストアといえば Oculus Store ですが、2020年12月現在、Oculus Store で Quest アプリをリリースするには厳しい企画審査を通過する必要があります。残念ながら、一般の個人開発者等には Oculus Store でのアプリのリリースは事実上不可能のようです。

(2021年2月)上記のページが消滅しており、Mogura VR の記事「【Oculus Quest】新アプリ配信システム「App Lab」 VRゲームが増加中 | Mogura VR」によれば

「App Lab」のローンチに伴い、Oculus Questの発売前から適用されていた、公式ストア向けのアプリ申請フォームは終了しています。フェイスブックによれば、公式ストアでの販売を希望する場合も、「まずはApp Labに注力していただきたい」とのこと。

とのことです。

App Lab

2021年2月より、Oculus Store には掲載されないものの、アプリの配信ページを直接開くとダウンロードできる App Lab という配信方法が追加されました。有料アプリも可能です。配信ページには、App Lab のアプリであるという注意書きが表示されます。Virtual Reality Check の要件が若干緩和されています。

App Lab のアプリは Oculus Store には基本的に出てこず、自然にダウンロードされることはないので、ウェブサイトやSNS、SideQuest 等、ユーザーがアプリをダウンロードする導線を自分で作る必要があります。

App Lab のアプリは、Oculus のダッシュボードに apk ファイルをアップロードして、配信ページ用のテキストや画像、動画等を登録すると申請できます。

(2021年4月)現在アプリの申請が殺到しており審査に1.5ヶ月ほどかかっているようです。また、審査について、Unityのキューブを表示するだけのアプリが App Lab の審査に通ったという報告がありました。Virtual Reality Check の要件さえクリアしていれば基本的にアプリの内容は問われないようです。

(2021年6月)著者制作の「VR刺身タンポポ」をApp Labに4月29日に提出したところ、審査の通過が6月11日で、43日かかりました(その間ダッシュボードのステータスの変更や連絡は一切なし)。参考までの実績値です。

App Labの審査に通過すると通知のメールが届き、即時ストアで購入・ダウンロードできるようになります。つまりリリースタイミングのコントロールはできません。

アプリの提出ボタンを押すと審査待ちの間ビルドの更新はできません(キャンセルして再提出することは可能ですが、待ち行列の最初に戻されるとのことです)。一度審査が通ったあとは自由にアップデート可能です。必要でしたらゼロデイパッチの準備をしておくとよさそうです。定期的な再審査をしているようです。

App Lab提出のコツについて下記エントリで解説されています。

apk ファイルの直接配布

ユーザーの Quest を開発者モードにしてもらって adb install コマンドでインストールすることが考えられます(いわゆるサイドローディング)。

SideQuest

サイドローディングのアプリを配布する非公式のアプリポータルとして SideQuest があります。PC にポータルソフトウェアをインストールして、Quest に apk ファイルをインストールできます。インディーゲーム販売サイト itch.io の決済システムを利用して有料 apk ファイルの配布も活発に行われています。

また、最近は App Lab のアプリポータルとしても機能しています。

Oculus Browser (WebXR)

ネイティブアプリではなく、Quest のウェブブラウザ(Oculus Browser)で動くウェブアプリとして開発することも考えられます。Oculus Browser は Chromium ベースのブラウザで、 WebXR サポートが積極的です。ハンドトラッキングの API にも対応しています。

WebXR アプリを作るには、 JavaScript で Three.js や A-Frame、Babylon.js 等の WebGL ライブラリを使用するほか、Unity でも WebXR Export を使用すると WebGL ビルドを WebXR に対応させられます。「Quest のブラウザで動くアプリを作るには」を参照してください。

Oculus Store および App Lab 向けアプリ作成時の Tips

Oculus の配信チャンネルを使用してアプリをリリースする際にチェックが必要なポイントについてまとめてみました。

ガイドラインに目を通す

Quest Virtual Reality Check (VRC) Guidelines というアプリのガイドラインがあり、Oculus Store や App Lab でリリースするアプリは各項目に準拠しているか審査されるため要チェックです。フレームレートの確保不要なマイク権限の削除Oculus ボタンを押したときの処理あたりがおそらく引っかかりやすいポイントだと思います。

パッケージ名を適切につける

Quest アプリは Unity の Project Settings の Player > Package Name をもとに識別されます(つまり、Package Name を変えると別のアプリとして認識されます)。

Oculus のダッシュボードにアップロードする apk ファイルをビルドする際に適切なパッケージ名を設定しておきます。通例、ドメイン名を逆順にした「com.[会社名].[プロダクト名]」のような名前をつけます。

App ID の設定

apk ファイルをビルドする際に App ID を設定する必要があります。Oculus のダッシュボードでアプリを作成して Getting Started API のページを開き、取得した App ID を Oculus > Platform > Edit Settings の Application ID のところに設定します。

アプリへの署名

apk ファイルをビルドする際に署名が必要です。Project Settings の Player > Publishing Settings > Keystore Manager… で署名ファイル(*.keystore)を作成して署名します。アプリのアップデートの際は同じ署名ファイルで署名する必要があります。設定したパスワードを忘れないように注意してください。プロジェクトファイルには保存されません。

詳しくは Android の keystore について検索してみてください。

Bundle Version Code の設定

Oculus のダッシュボードは Project Settings の Player > Other Settings > Bundle Version Code の数値でアプリのバージョンを識別しています。新しいバージョンの apk ファイルをアップロードするたびに Bundle Version Code の数値を増やしてビルドする必要があります。

Entitlement Check

Oculus Store で配信する apk ファイルを Oculus Store 経由でダウンロードしたときにのみ動くようにすることができます(VRC.Quest.Security.1)。

Oculus/Platform/Samples/EntitlementCheck に EntitlementCheck.cs というサンプルスクリプトが入っていますので、これを適当なゲームオブジェクトにアタッチして Exit App On Failure をオンにしてビルドします。上の項目の App ID の設定をしておく必要があります。

不要なマイク使用権限を削除する

Oculus Store および App Lab で配信する apk ファイルには必要最小限の権限を与える必要があります(VRC.Quest.Security.2)。

ちょっとした罠があり、Oculus Integration をインポートすると Unity がビルド後の AndroidManifest.xml に RECORD_AUDIO 等のパーミッションを自動的に付与してしまい、マイクを使用しないアプリなのに権限があるとリジェクトされるようです。

対処方法としては、もし Oculus/LipSync フォルダと Oculus/Avatar フォルダの機能を使用していなければこれらを削除するのが簡単です(特にリップシンクで使用されている libOVRLipSync.so は apkファイル に圧縮後で 11MB ほどあり、アプリサイズ削減の効果もあります)。

別の対処方法が以下で解説されています。

コマンドラインでアプリをアップロードするには

Oculus が提供するコマンドラインツールを使用して、ダッシュボードに apk ファイルをアップロードできます。CI に組み込んでおくと便利です。

ダッシュボードから取得した App ID と App Secret を使用して、

ovr-platform-util.exe upload-quest-build --app-id [App ID] --app_secret [App Secret] --apk Build.apk --channel alpha

のようにしてアップロードします。

提出前のアプリを他のユーザーに共有するには

ダッシュボードにアップロードしたアプリを、指定したユーザーがインストールできるようにしてテストしてもらうことができます。

ダッシュボードのリリースチャンネルの列の右の「…」から「チャネルの詳細を見る」を選択、ユーザーのタブで「ユーザーを追加」して、Oculus アカウントのメールアドレスを登録します。登録するだけでは駄目で、「アプリの申請」で表示されているアプリページの URL を該当ユーザーに共有してインストールしてもらう必要があります。

Quest のブラウザで動くアプリを作るには

WebXR Export を使用する

De-Panther 氏の WebXR Export を使用すると、Unity の WebGL ビルドを WebXR に対応させることができます。

Unity から出力した WebXR アプリは、以前(2020年4月頃)は Quest ではフレームレートが低かったり、起動に数十秒かかったりとなかなか厳しかったのですが、2021年3月時点では起動後少し待つと 90 fps 出るようになったり、起動時間も最短で数秒から済むようになったりと大幅に状況が改善しているようです。

WebXR Export の導入方法はこちらにあります。以下、サンプルシーンをビルドする手順のメモです(2021年3月時点)。

下記スライドも参照してください(古いので注意点くらいしか参考にならないかも……そのうちページのほうに移植します)。

なお、Unity 公式による WebXR サポートは、ロードマップによると Under Consideration(検討中)になっています(2021年6月現在)。

Oculus Browser でデベロッパーツールを使用するには

PC の Chrome からリモートのデベロッパーツールを開くことができます。Quest が接続されている(adb devices で表示される)状態で、PC の Chrome で chrome://inspect/#devices を開いて少々待つと Quest が検索表示されますので、inspect をクリックしてください。

ページを開く際も、URL を Quest のソフトウェアキーボードで入力するより PC でデベロッパーツールから入力するほうが楽だと思います。

Quest 本体の Tips

apk ファイルをインストールするには

Quest を開発者モードにした上で、adb コマンドで adb install するか、Oculus Developer Hub をインストールしてあれば apk ファイルを My Device タブの UNKNOWN SOURCE にドラッグするとインストールできます。

ガーディアンの表示を切るには

開発時にはガーディアンの表示を切っておくと便利です。イベント展示や、アリーナスケールのコンテンツの開発の際にもオフにします。

Quest の「設定 > 開発者 > ガーディアン」(Settings > Developer > Guardian)でオン・オフの設定ができます。オフにするときに安全のため警告メッセージが表示されます。また、Oculus Developer Hub をインストールすると My Device タブの Gurdian でオン・オフの設定が可能です。

一度スリープすると設定が元に戻ってしまうので、スマートフォンの Oculus アプリで「その他の設定 > 電源の設定 > オートスリープ」でスリープ時間を最長の15分にしておくといいでしょう。

Quest のストレージにアクセスするには

Quest を PC に接続すると USB ストレージとして認識されます。

もし認識されない場合は、スマートフォンの Oculus アプリの「その他の設定>開発者モード」をオフ・オンしていると Quest の画面に「データへのアクセスを許可」ダイヤログが出てきて見えるようになる場合があります。

ヘッドセット内の映像を中継表示するには

スマートフォン・タブレット

Oculus アプリにヘッドセット内の映像をミラーリング表示できます。Quest のメニューのシェアボタン>ミラーリングを選択してください。

ブラウザ

oculus.com/casting ページを開いてログインし、Quest のメニューのシェアボタン>ミラーリング>コンピューターでヘッドセットの中の映像をミラーリングできます(正方形表示)。フルスクリーン表示も可能です。

PC(USB 接続)

PC に Oculus Developer Hub をインストール・起動して、My Device タブで Cast Device を実行すると USB 経由で中継表示できます(正方形表示)。ウィンドウサイズを変更すると大きく表示できます。

scrcpy というツールもありますが、左右2画面の樽型表示になります。

スクリーンショットを撮るには

Oculus ボタンを押してダッシュボードを開き、「写真を撮る」ボタンを押すと数秒後に撮影音が鳴ってスクリーンショットが保存されます(正方形)。撮影した画像は Quest を PC に接続して /Oculus/ScreenShots フォルダから読み出せます。

右手コントローラーの Oculus ボタンを押しながらトリガーを引くことでもスクリーンショットが撮影できます(できないこともあるっぽい?)。

Unity の Android Logcat パッケージの Capture Screen のボタンを押すとスクリーンショットが撮影保存できます(左右2画面の樽型)。

Oculus Developer Hub の My Device タブの Screenshot のボタンでスクリーンショットを撮影できます(左右2画面の樽型)。下の Media Directory のボタンで保存先のフォルダが開きます。

動画をキャプチャするには

Oculus ボタンを押してダッシュボードを開き、「録画」ボタンを押すと動画の録画が開始します(正方形)。キャプチャした動画は Quest を PC に接続して /Oculus/VideoShots フォルダから読み出せます。

Oculus Developer Hub の My Device タブの Record Video のボタンで動画を撮影できます(左右2画面の樽型)。下の Media Directory のボタンで保存先のフォルダが開きます。

PC 本体の USB ポートを使用していると、Oculus Quest がなかなか充電されなかったり、Oculus Link が安定しなかったりする場合があります。

デスクトップ PC では USB カードを使用すると改善される場合があります。個人的にはこちらのカードを使用していて、Oculus Link 使用中でも安定して Quest 2 のバッテリーがすぐ充電されるようになりました(が、保証はしません)。

また、Windows のデバイスマネージャーで USB ハブの「電源の管理>電力の節約のために、コンピューターでこのデバイスの電源をオフにできるようにする」を切ると Oculus Link が安定するという話があります。

アカウントを変更するには

Oculus アプリとのペアリングを解除するにはファクトリーリセットが必要です。次の項目を参照してください。

ファクトリーリセットするには

スマートフォンの Oculus アプリの「その他の設定」から「出荷時設定にリセット」を選択します。

Quest 本体のみで工場出荷時の状態に戻すには、電源オフの状態から、音量マイナスボタンを押しながら電源ボタン長押しで電源を入れます。Oculus ロゴが表示されたら電源ボタンだけを離してください。メニューが表示されるので、Factory reset を選択して実行します。

Quest と PC を Wi-Fi 経由で接続するには

Oculus Developer Hub の My Device タブで ADB over Wi-Fi をオンにして USB ケーブルを抜きます。

Quest は屋外で使用できるの?

おそらくコントローラーの赤外線 LED が太陽光にかき消されるため、強い晴天下では Quest コントローラーは 6DoF トラッキングができなくなりました。なお、Windows Mixed Reality ヘッドセットは同条件で正常に動作しました。

Oculus Link・Rift S 編

Oculus Link・Rift S で動くアプリをビルドするには

Quest 単体向けのときと同様 Oculus Integration を使用します。Platform を「PC, Mac & Linux Standalone」にして、Project Settings > XR Plug-in Management で Standalone タブの Oculus のチェックが入っていることを確認してください。ビルドすると Oculus Link 用の .exe ファイルが作成されます。

あらかじめ Oculus ソフトウェアの設定を変更する必要があります。Oculus ソフトウェアのを起動して左の Settings を選択、General タブを選択して Unknown Sources をオンにします。この設定は一度変更すると Oculus のアカウントに保存されます。

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

展示でヘッドセットをかぶっていない人に PC ディスプレイで観客視点の映像を見せたり、アバターでプレイしている様子を表示したいといったことがあると思います。

Camera をシーンに追加し、余分な Audio Listener を削除した上で以下の設定を行います。レンダーパイプラインによって設定方法が変わります。

レガシーレンダーパイプライン

Camera コンポーネントの Target Eye を「None (Main Display)」に設定します。PC 側のカメラの Depth (priority) の数値をヘッドセット側のカメラより大きくしておく必要があります。もしくは、下記でヘッドセット側のカメラのミラー表示を無効にしてください(こちらのほうがより軽量なのではと思います)。

using UnityEngine.XR;
...
XRSettings.gameViewRenderMode = GameViewRenderMode.None;

URP

レガシーレンダーパイプライン同様ですが、Target Eye が表示されていないことがあります。インスペクターを右上メニューから Debug にすると Target Eye が見つかりますので、0 を設定すると Normal 表示のほうに「None (Main Display)」となって出てきます。

あるいは、Unity 2020.2 以降で preview パッケージをインストールできるようにして Universal RP パッケージを Version 10.0.0 以降に更新すると Target Eye が通常通り表示されます。

HDRP

Camera コンポーネントの XR Rendering をオフにします。

困ったとき

Quest で起動すると画面が真っ暗のまま動かない

Android Logcat で「Unable to find OculusXRPlugin」という赤文字のエラーが出ている場合、Unity の Oculus XR Plugin パッケージを更新すると動くようになる場合があります。

Build and Run でビルド後に Quest で起動しない

Unity のコンソールに

DeploymentOperationFailedException: No activity in the manifest with action MAIN and category LAUNCHER. Try launching the application manually on the device.

というエラーがもし出ている場合は、Oculus > Tools > Remove AndroidManifest.xml で AndroidManifest.xml を削除してみてください。

Quest 2 でアプリから外部ストレージにアクセスできない

Android 10 で外部ストレージのセキュリティが強化されました。Assets/Plugins/Android/AndroidManifest.xml に android:requestLegacyExternalStorage="true” を設定すると一時的にセキュリティを無効化できます。

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
  <application android:label="@string/app_name" android:icon="@mipmap/app_icon" android:allowBackup="false" android:requestLegacyExternalStorage="true">
  ...

詳しくは「Android Scoped Storage」等で検索してみてください。

その他の Tips

Quest プラットフォームのロードマップについて知りたい

Oculus Developer Center で、Quest プラットフォームの今後数ヶ月のロードマップと、High Priority Issues が公開されています(ログインが必要です)。

不具合を報告するには

開発者サポートページから報告します。以前あったバグトラッカーは削除されました。

過去のバージョンの Oculus Integration を参照するには

Oculus Integrationのリリースノートのページと、ダウンロードのページは、バージョン番号のところがプルダウンメニューになっていて、過去のバージョンが参照できます。

PlayerPrefs はどこに保存されているの?

通常の Android だと /data/data/[パッケージ名]/shared_prefs/[パッケージ名].xml にあるはずなんですが、Quest ではアクセスできず、また、アクセスできる場所にそれらしいファイルは見つかりませんでした……。アプリをアンインストールすると初期化できます。


参考リンク

書いた人:こりん(@korinVR
VR開発メモトップ