» VR開発メモトップへ

Unity + Meta Quest開発メモ

最終更新日:2023年11月09日

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

更新履歴

(2023年11月9日)Meta XR Utilities SDKからMeta XR Core SDKへの変更を反映
(2023年10月16日)Quest Linkの開発者ランタイム機能をオンにすると接続が遅くなるのを注記、「アプリを他のユーザーに共有するには」を更新
(2023年10月15日)「Questの機種を判定するには」を追加
(2023年10月14日)「MRアプリを作るには」の項目を作成、「部屋と干渉するMRアプリを作るには」を仮追加、「Building Blocksについて」を追加
(2023年10月10日)カバー画像をMeta Quest 3に変更


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

UnityのQuest対応の概要

Meta QuestのアプリはUnityを使用して開発できます。QuestはAndroidベースですので、Androidアプリとしてビルドします(Google Playが入っていないため要注意です)。

Unityの標準機能だけでもQuestで動くVRアプリは開発できますが、Quest固有の機能やMetaプラットフォームの機能を利用する場合はMetaのSDKであるOculus Integrationを使用します。以降本ページではOculus Integrationベースで解説します。

開発用のPC・OSは何を使えばいいの?

Quest開発にはWindows(10または11)のゲーミングPCがおすすめです。アプリ開発中にUnityで再生ボタンを押すだけでQuest Linkで動作を確認できます。macOSや、ゲーム用のGPUを搭載していないWindows機では、毎回ビルドしてQuest実機に転送しないとVRで確認できません。

下記ページにQuest Linkが動作するWindows PCの推奨スペックが掲載されています。英語からの翻訳ページのため「X」の印が日本語の「○」の意味なので注意してください。

Unityのバージョンはどれを使えばいいの?

Oculus Integrationが対応しているUnityのバージョンは Set Up Development Environmentのページで確認できます(日本語ページは情報が古い場合があるので注意してください)。2023年10月現在、Unity 2021.3以降が必要です。

Unity 2020.3から2021.3にかけてQuest開発に関連するビルドやインポートの高速化が多数入ってきているため、Unity 2021.3以降にすると開発が比較的快適になります。

Unity 2022以降でビルドその他のトラブルが多数発生しています。「Unity 2022でのトラブル」を参照してください。

Unity 2021から2023にかけてURPでパフォーマンスが大幅に低下する問題が発生しています。URPを使用する場合は2020に留まるようにするか(大丈夫かは要検証)、ビルトインレンダーパイプラインを選択したほうがいいかもしれません。

Questを開発者モードにする

Questで自作のアプリが動くようにするには、「開発者モード」にする必要があります。スマートフォンにインストールしたMeta Questアプリの右下の「メニュー(Menu)」をタップし、「デバイス」で接続されているデバイスを選択すると「ヘッドセットの設定」の中に「開発者モード」がありますのでオンにしてください。

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

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

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

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

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

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

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

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 パッケージがインストールされます。

パッケージ名をつける

QuestアプリはProject SettingsのPlayer > Package Nameをもとに識別されます(つまり、Package Nameを変えると別のアプリとして認識されます)。通常、ドメイン名を逆順にした「com.[会社名].[プロダクト名]」のような名前をつけます。

ビルド・実行する

試しにOculus/VR/ScenesフォルダのControllerModelsシーンを開いてみてください。

Build Settingsを開きQuestを接続してBuild and Runボタンを押し、適当な.apkファイル名を指定するとビルドと実機への転送が始まります。初回のビルドはかなり長い時間がかかりますが、2回目以降はずっと短い時間で済むようになります。成功するとアプリが起動し、コントローラーで操作ができます。

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

  • Oculus > Tools > Project Setup Toolでプロジェクトの問題がレポートされるので確認します。
  • Window > Package ManagerでXR Plug-in ManagementパッケージとOculus XR Pluginパッケージがインストールされているか、バージョンが新しいかを確認します。
  • Edit > Project SettingsでXR Plug-in ManagementのOculusとInitialize XR on Startupのチェックが入っているかを確認しします。
  • Oculus Integrationのバージョンが新しいことを確認します。

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

ビルドして実機に転送したアプリは、Questに格納されていていつでも実行できます。

場所が分かりにくいのですが、「アプリ(Apps)」メニューから上の「アプリを検索(Serach apps)」をクリックして、「すべて(All)」を「提供元不明(Unknown Sources)」に変更すると出てきます。

個々のアプリの右端の「…」ボタンからアンインストールもできます。

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

WindowsにOculusソフトウェアをインストールしてQuestをQuest Linkで接続すると、Unityのエディタ上でシーンをVRで再生できます。ビルドしなくても動作確認ができるため開発が楽になります。

Oculusソフトウェアの 設定 > ベータ > 開発者ランタイム機能 をオンにすると、Quest Linkでカメラパススルーも動作させられます。

Questのログ出力を見るには

Quest実機のエラー出力等を確認するために、UnityのAndroid Logcatパッケージをインストールしておくと便利です。Window > Package Managerで左上のPackagesをUnity Registryにすると出てきます。

Build and Runすると自動的にデバイスに接続してUnityのウィンドウにログが表示されます。キーワードでフィルタをかけたり、スクリーンショットを撮ることもできます。

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

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

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

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

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

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

Meta Quest Developer Hubについて

Meta Quest Developer Hubという開発用のポータルツールが提供されています。ヘッドセット内の映像の中継表示や、ガーディアンや近接センサーの無効化、アプリの起動やアンインストール、Quest Linkの起動、Metaが提供するツールのダウンロード、ダッシュボードへのアプリのアップロード等が簡単にできるようになっていますので、インストールしておきましょう。

なお、Meta Quest Developer Hubはadbコマンドが組み込まれており、UnityもデプロイやAndroid LogcatでUnity組み込みのadbコマンドを使用するため、両方同時に使用しようとすると衝突して正常に動作しません。たとえばUnityで下記のようなエラーが出ます。

Multiple ADB server instances found, the following ADB server instance have been terminated due to being run from another SDK. Process paths:
C:\Program Files\Oculus Developer Hub\resources\bin\adb.exe

対策として、Meta Quest Developer HubでUnity組み込みのadbコマンドを使用するように設定します。Meta Quest Developer HubのSettings > General > ADB PathのEditを押し、Detected ADB Clientsから使用しているUnityのバージョンのadb.exeを設定して右下のRestart MQDHで再起動してください。

Meta XR SDKのUnityパッケージ移行について(v59以降)

(2023年11月)SDKのv59以降、Oculus Integrationは非推奨になりました。個別の機能のパッケージをAsset Storeからインポートする方法に変更になっています。

SDK一式がAsset Storeに公開されています。Meta XR Core SDKが最低限必要なコアパッケージです。Asset Storeからインストールすると、プロジェクトのPackages/Meta XR Core SDKにOculus IntegrationのVRフォルダとほぼ同じものが入っています。さらに、Package ManagerのMeta XR Core SDKパッケージのSamplesからサンプルシーンをインストールできます。

後の手順は基本的にOculus Integraionと同様です。必要な場合は他のパッケージを追加でインストールします。

Meta XR Simulatorについて

ヘッドセットがなくてもMeta Questの動作をシミュレートできるOpenXRランタイムが提供されています。

Asset StoreのMeta XR Simulatorでパッケージをインストールしたのち、メニューのOculus > Meta XR Simulator > Activateを有効にしてUnityの再生ボタンを押すと下のようなシミュレーター画面が開きます。

[、]キーで操作対象(ヘッドセットと左右モーションコントローラー)を変更して、以下の要領で移動操作ができます(一部のみ。ボタン操作等の詳細はMeta XR SimulatorのInput Bindingsの表示を確認してください)。

キーボード・マウス 操作
マウス右ドラッグ 回転
マウス中ドラッグ 上下左右移動
マウス左クリック トリガー
WASD 前後左右移動
Q/E 左右ロール

また、MR開発をサポートするSynthetic Environment Serverという機能があり、Oculus > Meta XR Simulator > Synthetic Environment Serverでサーバーを立ち上げておくとカメラパススルー等をテストできます。その他さまざまな開発補助機能が用意されています。

Building Blocksについて

Oculus Integration 57.0からBuilding Blocksという機能が入りました。Oculus > Tools > Building Blocksで下のようなウィンドウが開いて、必要な機能をクリックしていくだけでカメラリグを組み立てたり自動設定してくれたりするようになっています。

出たてなのでどれくらい安心して使えるかは分かりませんが、最初のセットアップが簡単になるので試してみる価値はありそうです。


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

グラフィックス設定

レンダリング・パイプライン

ビルトインとUniversal Render Pipelineのどちらでも大丈夫です。フォワードレンダリングが基本になります。OpenXRバックエンドでは必ずリニアカラーにする必要があります。

アンチエイリアス

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

Vulkanを使っても大丈夫?

Graphics APIのVulkan対応について、Oculus XR PluginのChangelogに以下の記述があり、Unity 2021.2以降・Oculus XR Plugin 1.11.0以降で安定版になったようです。

Vulkan is no longer experimental for Oculus on Android in Unity 2021.2 and higher, and will no longer be removed from your graphics settings in those versions.

Vulkanのステータスについては「Vulkan APIで既知の問題」のページが参考になります。

その他

  • 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プレハブ(v59以降はPackages/Meta XR Utilities/Prefabs/OVRCameraRig)をシーンに配置します。プレハブにアタッチされているOVRCameraRig.csによって、OVRCameraRigの孫のCenterEyeAnchorオブジェクトがヘッドセットに追従します。

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

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

IL2CPP

Metaにアプリを提出する場合はProject SettingsでScripting BackendをIL2CPPにしてARM64でビルドする必要があります。が、IL2CPPビルドはとても時間がかかるので、開発中や、ストアにリリースするつもりのない制作物ではMonoでいいかもしれません(MonoビルドはIL2CPPビルドと比べて実行速度がだいぶ遅くなります。また、カメラパススルー等、IL2CPPビルドにしないと動かない機能があるので注意してください)。

adbコマンドについて

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

adbコマンドは下記ページからダウンロードできるほか、Unityのインストール時に同梱されるものを使用することもできます。UnityのAndroid Logcatパッケージをインストールして、Android Logcatのウィンドウの右上のTools > 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 ip addr

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

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

Touchコントローラーは、主に外周リング部に埋め込まれた赤外線LEDをQuest本体の赤外線カメラで位置トラッキングする仕組みになっています。バッテリーは左右それぞれ単三電池一本です。エネループ等も使用できます。

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

OVRCameraRigのLeftControllerAnchorおよびRightControllerAnchorにOVRControllerPrefabをアタッチして、左手のOVRControllerPrefabのControllerをL Touchに、右手のOVRControllerPrefabのControllerをR Touchに設定してください。

なお、Oculus/VR/Scenes/ControllerModelsシーンに上記セットアップ済みのOVRCameraRigが入っています(Meta XR Utilitiesの場合はPacakge ManagerのMeta XR UtilitiesパッケージのSamplesからSample ScenesをインストールするとAssets/Samples/Meta XR Utilities/{バージョン}/Sample Scenesにシーンが入っています)。

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

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

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

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

コントローラーの位置は、OVRCameraRigのLeftHandAnchor、RightHandAnchorのTransformを参照するほか、OVRInput.GetLocalControllerPosition / GetLocalControllerRotationで取得することもできます。

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

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);
        }
    }
}

コントローラーでUGUIを操作するには

まず、Canvasで作成したUIをワールド空間に配置します(CanvasのRender ModeをWorld Spaceにして、Scaleを0.01等にして小さくします)。

CanvasにアタッチされているGraphics Raycasterの代わりにOVR Raycasterをアタッチし、Assets/Oculus/SampleFramework/Core/DebugUI/PrefabsにあるUIHelpersプレハブをシーンにドロップするとコントローラーでポイントして操作できるようになります。

さらに、UIHelpersの中にあるLaserPointerオブジェクトのLine Rendererをオンにするとレイが表示されます。

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

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

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

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

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

エディタ上でハンドトラッキングを使用するには

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

Questの「設定 > デバイス > ジェスチャーコントロールとコントローラー」の「手とコントローラーの自動切り替え」をオンにした状態で Quest Link を有効にしてください。このオプションがオフだと、ハンドトラッキングをオンにしていてもQuest 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 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 レベルについて」を参照してください。

アイトラッキングを使用するには

アイトラッキングを使用するには、OVRCameraRigのOVR ManagerのGeneral > Eye Tracking SupportをRequiredに変更し、下のPermission Requests On Startupを開いてEye Trackingをオンにします。これにより、アプリ起動時に確認のメッセージが表示されるようになります。

手っ取り早い使い方としては、キャラクターの眼球に相当するオブジェクトにOVREyeGazeをアタッチして、EyeをそれぞれLeftとRightに設定、Apply Rotationをオンにして、Tracking ModeをHead Spaceにすると視線の向きにあわせて眼球のオブジェクトが回転します。

Quest Linkでアイトラッキングを使用したい場合は、Oculusソフトウェアの「設定 > ベータ > 開発者ランタイム機能」をオンにして、その下の「Oculus Link経由でのアイトラッキング」をオンにします。

Meta公式のサンプルおよび解説ページがあります。そこそこ重厚なサンプルで、動作させるにはレイヤー設定とプリロードシェーダーの設定(指示ダイアログが表示されます)、パッケージからProject内へのSceneのコピー等が必要です。

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

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

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

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

Unity 2021.3.2にQuestのソフトウェアキーボード関連の修正が含まれているようです。

XR: Fixed Oculus Quest software keyboard not showing up on InputField interaction. (1417991)

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についてはまだ記載がないようです)。

Questの機種を判定するには

OVRManager.systemHeadsetTypeで使用しているQuestの機種が取得できます。SystemHeadsetType列挙型でMeta_Quest_3、Oculus_Quest_2、Meta_Quest_Proのいずれかが返ってきます。

Meta Quest 3を判定するにはOculus XR Pluginを4.1.1以上にして、OVR ManagerのTarget DevicesでQuest 3にチェックを入れる必要があります。

Quest 3でQuest 3のチェックを入れていないとMeta_Quest_Proが、Quest ProでQuest Proのチェックを入れていないとOculus_Quest_2が返ってくるので注意してください。

Quest Linkで接続している場合はMeta_Link_Quest_3、Oculus_Link_Quest_2、Meta_Link_Quest_Proのいずれかが返ってきます。

MRアプリを作るには

パススルー機能を使うには

(要更新)

Questアプリ内でヘッドセット外の光景を立体視で表示できます。Quest 2ではモノクロ表示、Quest ProとQuest 3ではフルカラー表示です。

以下、ひとまずパススルーのサンプルシーンをビルドして動かす方法のメモです。

  • Assets/Oculus/SampleFramework/Usage/Passthroughシーンを開く
  • OVRCameraRigを選択して、OVRManagerでQuest Features > Generalの"Passthrough Capability Enabled"と、その下の"Enable Passthrough"をオンにする
  • スクリプティングバックエンドをIL2CPP/ARM64にする

PassthroughシーンではOVRCameraRigにOVR Passthrough Layerがアタッチされていて、これによりパススルー映像が描画されます。インスペクタの設定でパススルー映像にエッジを追加したり色の調整をしたりできます。

なお、Quest 2では本体の撮影・録画機能でカメラパススルー映像は記録されません。パススルー映像をキャプチャする方法は「動画をキャプチャするには」を参照してください。なお、Quest 3、Quest Proでは本体でパススルー映像を記録可能です。

Quest Linkでパススルー表示するには

PCのOculusソフトウェアの 設定 > ベータ > 開発者ランタイム機能 をオンにして、さらに下に出てくる「Oculus Link経由でのパススルー」をオンにします。

上記の必要な設定をした上で、シーンを普通に再生するとパススルー表示されます。OculusソフトウェアやUnityを再起動しないと有効にならないことがあるようです。

アプリ実行中にパススルー表示をオン・オフするには

Passthrough Capability Enabledをオンにした上で、その下のEnable Passthroughを切り替えます。起動時にパススルー表示しない場合はオフにしておきます。C#からは

OVRManager.instance.isInsightPassthroughEnabled = true;

のようにしてアクセスできます。

画面の一部をパススルー表示するには

MixedReality/SelectivePassthroughシェーダーを使用します。SurfaceProjectedPassthrough、PassthroughHands等のサンプルがあります。

部屋と干渉するMRアプリを作るには

(書きかけです)

部屋のメッシュを取得するMesh APIと、奥行きを取得するDepth APIがあり、通常両方を組み合わせて使用することとされています。

部屋の形状を取得するには

Questの設定 > 物理的空間 > スペースの設定 > スペースの設定で認識させた部屋の形状をUnityで取得してコリジョン等に使用できます(Mesh API)。

OVRManagerのAnchor SupportをEnabled、Scene SupportをEnabled/Required、Passthrough SupportをRequiredに設定します。さらにOculus > Tools > Create Store Compatible AndroidManifest.xmlを実行し、Assets/Plugins/Android/AndroidManifest.xmlを開いてUSE_SCENE、USE_ANCHOR_API、PASSTHROUGHが有効になっていることを確認します。

OVRSceneManagerプレハブをシーンに配置します。壁・床・天井はPlane Prefab、メッシュやボリュームはVolume Prefabに設定したプレハブが生成されます。Assets/Oculus/SampleFramework/Usage/SceneManager/Prefabsにサンプルのプレハブがありますのでこれらを使用します。たとえばInvisiblePlane、InvisibleVolumeを設定すると何も見えない(コリジョンだけある)オブジェクトが生成されます。

Mesh APIの動作はQuest Linkでも確認できます。

現実空間の物体でUnityのオブジェクトを遮蔽するには

現実空間の深度情報を取得するDepth APIのサンプルプロジェクトがGitHubで公開されています。

Depth APIは現在Quest 3の実験的な機能で、動作させるには下記adbコマンドを実行する必要があります。

adb shell setprop debug.oculus.experimentalEnabled 1

DepthAPI-BiRP(ビルトインレンダーパイプライン版)かDepthAPI-URP(URP版)のプロジェクトを開いてビルドすると、確認のダイヤログが表示され実行されます。Aボタンでオクルージョンの有無と種類(ハードオクルージョンとソフトオクルージョン)を切り替えられます。ソフトオクルージョンのほうがGPU負荷がかかるようです。オクルージョン表示はアプリケーションのバッファのアルファチャンネルに描き込んで、パススルー映像の合成に使用しているようです。

現在Depth APIの使用には以下の要件があるようです。

  • Unity 2022.3.1または2023.2
  • Oculus XR Pluginのバージョン4.2.0-exp-env-depth.1を使用
  • OVRManagerでScene SupportをEnabledにして、Experimental Features Enabledをオン
  • Graphics API=Vulkan、Stereo Rendering Mode=Multiview

Depth APIでオクルージョン、Mesh APIでコリジョンという使い分けが推奨されています。深度情報の取得にはMesh APIのメッシュデータとの統合(depth fusion)が行われるため、スペースを認識させていないと不正確になるものの、スペースのデータなしで使うこともできなくはないようです。

Depth APIへの低レベルアクセスをする方法が下記ページの下のほうに載っています。

空間アンカーを使うには

空間アンカーはセッション間で保持される物理空間の位置です。パススルー機能と組み合わせることで、Mixed Realityのコンテンツを現実世界の同じ位置に復元できます。空間アンカーはUInt64のIDを持っており、作成、保存、削除ができます。

以下、とりあえず空間アンカーのサンプルシーンを動かす方法のメモです。

  • Assets/Oculus/SampleFramework/Usage/SpatialAnchorシーンを開く
  • OVRCameraRigを選択して、OVRManagerでQuest Features > Generalの"Passthrough Capability Enabled"をオンにする
  • さらにExperimentalの"Experimental Feature Enabled"をオンにして、“Spatial Anchors Support"をEnabledにする
  • スクリプティングバックエンドをIL2CPP/ARM64にする

Questの実験的なシステムプロパティを有効にする必要があります(このプロパティはQuestを再起動するとリセットされます)。

  • Package Managerを開いてUnity RegistryからAndroid Logcatをインストールする
  • Window > Analysis > Android Logcatを開く
  • Android Logcatウィンドウの右上のTools > Open Terminalでターミナルを開く
  • 「adb shell setprop debug.oculus.experimentalEnabled 1」を実行

パススルー機能が動作している必要があります。動かない場合は、上の「パススルー機能を使うには」も確認してみてください。

空間アンカーから離れた場所にオブジェクトを描画すると位置精度が低下するようです。3メートル以内にすると安定するとのことです。

ビルドを速くするには

新しいバージョンのUnityを使用する

Unityは2020.3から2021.2にかけて多数のビルド・インポートの高速化が入ってきています。特にQuestで使用するIL2CPPやテクスチャのASTC圧縮が何倍も速くなっていますので、できるだけ早く2021.2以上に上げるのが個人的におすすめです。

Unity 2021.2ではBuild SettingsのIL2CPP Code GenerationをFaster (smaller) buildsにするとIL2CPPビルドがさらに倍速くなります。また、Project Settings > Editor > Parallel Importでテクスチャやモデルの並列インポートを有効にできます。

Monoビルドにする

Oculusにアプリを提出する場合はIL2CPP/ARM64でビルドする必要がありますが、開発中はMono/ARMv7にするとビルドが速くてとても楽です。実行速度が遅くなるほか、カメラパススルー等、IL2CPPにしないと動かない機能があるため注意が必要です。

Skip Unneeded Shaders

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

Assets/Oculus/OculusProjectConfigを選択して、Build SettingsのSkip Unneeded Shadersをチェックしてください。

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

Unity Acceleratorを使用する

アセットの変換をキャッシュするUnity Acceleratorを使用することで、同じアセットを繰り返しインポートするときに変換処理を走らせなくて済むようになります。下記ページを参照してください。

USB 3.0ケーブルを使う

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

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” がキャッシュされます。ビルド時間が数割短縮されます。

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

パフォーマンス最適化

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

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

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

QuestのCPU・GPUについて

Questの機種ごとの搭載SoCは以下の通りです。

機種 SoC
Meta Quest Snapdragon 835
Meta Quest 2 Snapdragon XR2
Meta Quest Pro Snapdragon XR2+
Meta Quest 3 Snapdragon XR2 Gen 2

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

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

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

CPU・GPUレベルについて

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

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

OVRManager.suggestedCpuPerfLevel = OVRManager.ProcessorPerformanceLevel.SustainedLow;
OVRManager.suggestedGpuPerfLevel = OVRManager.ProcessorPerformanceLevel.SustainedHigh;

以下は参考までに、Quest 2で空シーンでCPUとGPUのレベル設定を切り替えたときのOVR Metrics Toolのプロセッサーレベルと周波数の表示です。

suggestedCpuPerfLevel CPU L CPU F
PowerSavings 0 710
SustainedLow 2 1171
SustainedHigh 3 1382
Boost 4 1489
suggestedGpuPerfLevel GPU L GPU F
PowerSavings 0 305
SustainedLow 1 400
SustainedHigh 2 441
Boost 3 490

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

注意が必要なのがパフォーマンスを計測するときで、レベルによってCPU・GPUの使用率の数値が変動します。使用率を比較する場合は明示的にレベル設定をして、同じレベルで比較する必要があります。

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

Questのパフォーマンス最適化に使用できるツールはMeta、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のドキュメントは下記ページにあります。

また、OVR Metrics Toolは、Meta Quest Developer HubのDevice ManagerタブにあるMetrics HUDでオン・オフしたり表示項目を変更することもできます。

Unity のプロファイラー

Unityのプロファイラーを使用するとQuest実機の処理内容と実行時間を確認できます。Build SettingsでDevelopment Buildを有効にした上でビルド・実行して、Windows > Analysis > Profilerでプロファイラーを開き、上部のPlay Modeと表示されているところでQuestを選択してください。

CPU Usageの列が約13.888…ミリ秒(72fpsの場合)で安定し綺麗な長方形になっているのがCPUまたはGPUの処理オーバーが起きていない正常な状態です。また、画像のようにTimeline表示にしてQuestの1フレームのCPUの実行処理の詳細を調べられます。

RenderDoc

RenderDocはオープンソースのフレームデバッガで、各種グラフィックスAPIで描画コールの実行をトレースしたり、おおまかな実行時間を計測表示できます。

Quest用に機能が追加されたRenderDoc Meta Forkがあり、Meta Quest Developer Hubからインストールできます(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 の時計のアイコンをクリックすると描画コールごとのおよその実行時間が出てくる

Perfetto

さらに詳しくQuest実機のパフォーマンス状況を追いかけたいときは、Google製のPerfettoというツールがあります。Meta Quest Developer Hubに統合されており、Performance Analyzerタブで表示できます。

Snapdragon Profiler

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

その他の参考になる情報

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

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

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

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

描画解像度について

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

周辺視野の解像度の変更

Fixed Foveated Rendering

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

下記APIで使用できます。

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

現在のレベルは、OVR Metrics ToolのSTATSでFoveation Levelの項目をオンにするか、Android Logcatの下記表示のFovのところを見ると確認できます。

FPS=72/72,Prd=39ms,Tear=0,Early=0,Stale=0,Stale2/5/10/max=0/0/0/0,VSnc=0,Lat=-3,Fov=0,CPU4/GPU=4/3,...

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

Dynamic Foveated Rendering

描画負荷によって自動的にFoveated Renderingのレベルを変動させます。通常はオフの状態で、負荷が増えてフレーム落ちしそうになるとレベルが徐々にfoveatedRenderingLevelで指定したレベルまで上がっていき、負荷が減ると徐々にレベルが下がってオフに戻っていきます。

OVRManager.useDynamicFoveatedRendering = true;
OVRManager.foveatedRenderingLevel = OVRManager.FoveatedRenderingLevel.High;

adbコマンドで一時的に設定を変更する

下記adbコマンドで、アプリの外からFoveated Renderingの設定を変更できます。パフォーマンス検証に使用できます。Questを再起動するとリセットされます。

$ adb shell setprop debug.oculus.foveation.level 0 (0~4)
$ adb shell setprop debug.oculus.foveation.dynamic 0 (0:オフ/1:オン)

視野全体の解像度の変更

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

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

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

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

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

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

圧縮フォーマットを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を上げると綺麗に表示されます。

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

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

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に値を代入して実際にリフレッシュレートが変更されるまでは値が反映されていないため注意してください。

リフレッシュレートが変わるとOVRManager.DisplayRefreshRateChangedイベントが飛んできます。

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

OVRScreenFadeを使う

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

フェードアウトするにはFadeOutメソッド、フェードインするには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ですが、残念ながら個人開発者等がアプリを自由にリリースすることはできません。一般に、実績ある開発会社やパブリッシャーがMeta社と直接交渉する必要があります。ただ、次で説明するApp LabでリリースされたアプリがOculus Storeに昇格した事例がいくつかあります。多数のユーザー評価がついていてかつ非常に高評価なアプリが昇格しており、ハードルはとても高いです。

OculusのFAQに関連する説明があります。

App Lab

2021年2月より、Oculus Storeには掲載されないものの、アプリの配信ページを直接開くとダウンロード・購入できるApp Labという配信方法が追加されました。

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

アプリ審査があり、Virtual Reality Checkの技術的要件をクリアする必要があります(Oculus Storeより若干緩和されているようです)。Unityのキューブを表示するだけのアプリが審査に通ったという報告があり、Virtual Reality Checkさえクリアしていればアプリの内容は基本的に問われないようです(代わりにレーティングのチェックがあります)。審査待ちの時間があります。

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

App Labの審査に通過すると通知のメールが届き、ストアで購入・ダウンロードできるように なります。なお、ストア提出時にRelease TypeをScheduled Releaseに変更しておくと、審査通過後にリリース日時を指定してリリースすることができます。

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

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

apkファイルの直接配布

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

SideQuest

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

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

Meta Quest Browser (WebXR)

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

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

アプリ提出時のTips

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

ガイドラインに目を通す

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

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

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

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

App IDの設定

apkファイルをビルドする際にApp IDを設定する必要があります。Oculusのダッシュボードでアプリを作成して左の列からAPIのページを開き、取得したApp IDをOculus > Tools > Oculus Platform ToolsのOculus 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

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

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

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

apkファイルには必要最小限の権限を与える必要があります(VRC.Quest.Security.2)。

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

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

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

マルチプレイヤーアプリの注意

Questの新しい審査基準で、2023年5月31日から、マルチプレイヤーのゲーム等でOculusボタンから不適切なコンテンツやユーザーを通報できるようにする必要があるとのことです。User Reporting Pluginを使ってレポート機能を起動できるようにするか、2023 Q1からダッシュボードで提供されるUser Reporting Serviceを使用とのことです。

ダッシュボードにアプリをアップロードする

上記設定を行ってビルドしたapkファイルをMeta Quest Develoepr Hubにドラッグすると、ダッシュボードにアップロードするためのダイアログが表示されます。

または、Metaが提供するコマンドラインツールを使用してアップロードすることもできます。ビルドスクリプトや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

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

個人情報の取り扱いについて

プライバシーポリシーを作成してURLでアクセスできるようにしておく必要があります。

また、Metaのプラットフォーム機能を使用するには、個人情報を適切に扱っていることを確認するデータ保護評価(Data Use Checkup)という手続きに従う必要があります。1年に一度再認定を受ける必要があり、従わないとアプリのプラットフォーム機能が無効化されたり、ストアから削除されたりする可能性があります。有効期限が切れる数週間前にMetaからメールで通知が届きますのでよく確認しておきましょう。

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

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

ダッシュボードの左列の「リリースチャンネル(Release Channels)」をクリックし、該当チャンネルをクリックして「チャネル設定(Channel Settings)」の「URLによるユーザーへのアクセス付与(Grant access to users by URL)」で招待用のURLを発行できます。

URLを受け取ったユーザーは、Metaアカウントにログインした上でURLのアプリのページを開いて、「Join」ボタンをクリックして少々待つとデバイスのアプリページに表示されインストールできるようになります。

Quest本体のTips

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

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

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

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

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

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

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

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

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

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

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

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

ブラウザ

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

PC(USB接続)

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

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

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

Oculusボタンを押してダッシュボードを開き、「写真を撮る」ボタンを押すと数秒後に撮影音が鳴ってスクリーンショットが保存されます(正方形)。撮影した画像はQuestをPCに接続して/Oculus/ScreenShotsフォルダから読み出せます。また、Meta Quest Developer HubのFile Managerタブで読み出すこともできます。この方法ではカメラパススルーの表示は記録できません(真っ暗になります)。

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

Meta Quest Developer HubのDevice ManagerタブのScreenshotのCaptureボタンでスクリーンショットを撮影できます(左右2画面の樽型)。カメラパススルーの表示も記録できます。

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

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

Quest本体の録画機能

Oculusボタンを押してダッシュボードを開き、「録画」ボタンを押すと動画の録画が開始し、画面の右上にドットが表示されます。もう一度ボタンを押すと録画を終了します。

キャプチャした動画はQuestをPCに接続して/Oculus/VideoShotsフォルダから読み出せます。また、Meta Quest Developer Hub のFile ManagerタブのVideosフォルダで読み出すこともできます。

生成される動画ファイルは片目1画面の正方形で、解像度1024x1024、約24fpsです。

フレームレートの高い動画を撮影したい場合は、adb shell setprop debug.oculus.fullRateCapture 1を実行すると72fpsで録画できます。0を設定すると元に戻ります。

Meta Quest Developer Hub

Meta Quest Developer HubのDevice ManagerタブのRecord Videoボタンで動画を撮影できます。キャプチャを終了すると動画ファイルが C:¥Users¥[ユーザー名]¥AppData¥Roaming¥odh¥captures に保存されます。

生成される動画ファイルは左右2画面の樽型で、解像度3664x1920になります。カメラパススルーの表示も記録できます。Recordボタンの横の設定ボタンでビットレートを設定できます。デフォルトの5Mbpsでは激しい(見るに耐えないレベルの)ブロックノイズが乗るため、高いビットレートに上げたほうがいいです。単眼(Single Eye)で録画する設定もありますが、手元ではエラーで録画が開始できないようです。

adb shell screenrecord

adb shell screenrecordで動画を録画できます。生成される動画ファイルは左右2画面の樽型で、解像度3664x1920、約72fpsです。カメラパススルーの表示も記録できます。

ビットレート40Mbpsで5秒間録画してPCにダウンロードするコマンド例を示します。

$ adb shell screenrecord --time-limit 5 --bit-rate 40000000 /sdcard/video.mp4
$ adb pull /sdcard/video.mp4 [ダウンロード先フォルダ]

Quest Linkを安定接続するには

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

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

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

どうしても安定しない場合、Air Linkを試してみるのもおすすめです。

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

Meta Quest Developer Hub のDevice ManagerタブでADB over Wi-FiをオンにしてUSBケーブルを抜きます。この状態でアプリをビルド・デプロイしたり、Quest Link(Air Link)を使ったりすることも可能です。

PCでQuestのブラウザのURLを入力するには

QuestのソフトウェアキーボードでURLを入力するのは大変です。

Meta Quest Developer HubのDevice ManagerタブのDevice Actions > Meta Quest BrowserにURLを入力して簡単にページを開くことができます。

下記adbコマンドでも行けます。

$ adb shell am start -n "com.oculus.vrshell/.MainActivity" -d apk://com.oculus.browser -e uri https://framesynthesis.jp/

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

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

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

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

ファクトリーリセットした後、Meta Questアプリとペアリングして、チュートリアル動画を見てWi-Fiに接続すると、Quest本体とコントローラーのソフトウェアが最新版に更新されます。使用できるようになるまで10分程度は見ておいたほうがよさそうです。

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

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

ジェスチャーコントロールを有効にできない

設定 > 動きのトラッキング > ジェスチャーコントロール をオンにしても通常表示されるダイアログが表示されず、ハンドトラッキングに切り替えられない場合があります。

インターネット接続が繋がっていないとこの現象が起きるようです。展示等でハンドトラッキングを使用する場合は、あらかじめジェスチャーコントロールを有効にして持っていきましょう。

Quest Link編

Quest Linkで動くアプリをビルドするには

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

アプリ起動後にVRモードを有効にしたい

Unity VR開発メモ(XR Interaction Toolkit + OpenXR Plugin)」の「アプリ起動後にVRモードを有効にしたい」と同様の方法で後からVRモードにできます。

ただし、OpenXR Pluginを使用する場合と異なり、Oculusソフトウェアが起動していない状態でVRモードを有効にしようとしても有効にならない(Oculusソフトウェアが起動しない)ようです。Oculusソフトウェアをあらかじめ起動しておく必要があります。

Unity 2022でのトラブル

Unity 2022.2以降で種々のトラブルが発生しているため大項目にまとめました。

Unity 2022.3でビルドできない

(2023年7月)以下のようなエラーでビルドが失敗することがあります。

A failure occurred while executing com.android.build.gradle.internal.tasks.CheckAarMetadataWorkAction

Project SettingsのTarget API Levelを33にするとビルドができるようになります。が、Questアプリは現在Target API Levelを32に設定することが求められており、33でビルドした.apkファイルはダッシュボードにアップロードできません。

対策としては、Target API Levelを32に設定した上で、Project Settings > Player > Publishing SettingsでCustom Launcher Grade TemplateをオンにするとAssets/Plugins/Android/launcherTemplate.gradeファイルが作られますので、以下の要領でSDKのバージョン33でコンパイルされるように設定します。

    // compileSdkVersion **APIVERSION**
    compileSdkVersion 33

この設定をした場合、C:\Program Files\Unity\Hub\Editor{Unity Version}\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platforms フォルダにandroid-33のSDKが存在しないとビルドできませんので注意してください。一度Target API Levelを33に設定してビルドするとダイアログが表示されてSDKがインストールできます。

Unity 2022.3でビルドするとクラッシュする

(2023年8月)Unity 2022.3でビルド後のアプリがQuestでクラッシュする場合があります。Android Logcatのログに

Cause: null pointer dereference

と出力されている場合、グラフィックスAPIをVulkanにすると改善されることがあります(必ずしもこれで安定するとは断言できず、上記エラーメッセージで検索すると結構深刻そうです)。

Oculusプラグインが勝手にオフになる

(2023年3月)Oculusプラグインをオンにできない(プロジェクトを開き直すと勝手にオフになる)現象が起きています。関連するパッケージが一斉にアップデートされているためか、なんらかの相性問題が発生しているようです。

原因の特定はできていないのですが、手元では、以下の要領で一通りアップデートをかけてプラグインの再設定をすると修復されました。

  • Unity 2022.2.9(以上)に上げる
  • XR Plugin Managemtentを4.3.3(以上)に上げる
  • Oculus XR Pluginを3.2.3(以上)に上げる
  • Assets/XRフォルダをいったん削除
  • Project Settings > XR Plug-in Managementを開いて設定しなおす

Unity 2022.2でビルドするとクラッシュする

下記の要領でXR Plugin Managementパッケージを4.3.1以降に更新すると上手くいくことがあります。ついでにAssets/XRフォルダを一度削除して、Project Settings > XR Plug-in Managementを開いて設定しなおしたほうがいいかもしれません。

Unity 2022.2でビルドが失敗する

もし下記のようなエラーでビルドが失敗する場合、Oculus > Tools > Create store-compatible AndroidManifest.xmlメニューで生成したAndroidManifest.xmlがAndroid 12に対応していないのが原因です。

A failure occurred while executing
com.android.build.gradle.tasks.ProcessLibraryManifest$ProcessLibWorkAction
Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8
...
android:exported needs to be explicitly specified for element <activity#com.unity3d.player.UnityPlayerActivity>. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined. See https://developer.android.com/guide/topics/manifest/activity-element#exported for details.

対処するには、Assets/Plugins/Android/AndroidManifest.xmlのactivityタグのandroid:nameのところに下の要領でandroid:exportedを追加します。

android:name="com.unity3d.player.UnityPlayerActivity" android:exported="true" ...

ダッシュボードへのアップロードが失敗する

Unity 2022.2でビルドしたapkファイルをovr-platform-util.exeでアップロードしようとすると以下のエラーが出ます。

ERROR: 認証中にAPKに問題が見つかりました。APKが「Application Manifest Requirements」の条件を満たしていることをご確認のうえ、もう一度送信してください。

* APKのインストール場所は「自動」(android: AndroidManifest.xml内のinstallLocation)でなくてはなりません。ドキュメントはこちらでご覧ください: https://developer.oculus.com/distribute/publish-mobile-manifest/

対処方法として、C:\Program Files\Unity\Hub\Editor\2022.2.9f1\Editor\Data\PlaybackEngines\AndroidPlayer\Apk\LauncherManifest.xml の android:installLocation=“preferExternal” を android:installLocation=“auto” に変更して管理者権限で保存し、ビルドし直すとアップロードできるようになります。下記スレッドで話題になっています。

ビルドのトラブル

ビルドは通るがQuestにインストールできない

Build and Runで下記のようなエラーが出てインストールできないときは、Questでアプリを一度削除してBuild and Runし直しすと上手くいくことが多いです(Android SDKを確認するようにというメッセージで紛らわしいのですが……)。

CommandInvokationFailure: Unable to install APK to device. Please make sure the Android SDK is installed and is properly configured in the Editor. See the Console for more details.

ビルド後に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を削除してみてください。

困ったとき

Rift Sでエディタを再生するとUnityがフリーズする

Oculusソフトウェアの設定で、「ベータ > 開発者ランタイム機能」がオンになっている場合はオフにしてみてください(「Oculus Linkが必要です」とあるので、それはそう……という感じです)。

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

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

不具合を報告するには

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

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

Oculus Integration SDKのページの右上のVERSIONをプルダウンして選択すると過去のバージョンがダウンロードできます。

Quest 1のアプリを開発するには

Oculus Integration 51.0以降でQuest 1のサポートが打ち切られているため、Quest 1向けに開発する場合は、Oculus Integration 50.0以前を使用する必要があります。過去のバージョンのOculus Integrationは、Oculus Integration SDKページ右上のVERSIONのプルダウンから選択してダウンロードできます。

また、Unity自体のQuest 1サポートはUnity 2021 LTSまでで、Oculus XR Plugin 3.xを使用するようにとのことです。

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

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

こちらのスレッドで話題になっています。


参考リンク

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