» VR開発メモトップへ

Unity + Meta Quest開発メモ

最終更新日:2024年03月19日

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

更新履歴

(2024年3月12日)URPでのFoveated Renderingについて何箇所か補足
(2024年3月7日)Snapdragonのゲーム開発者向けガイドのリンクを更新
(2024年2月28日)URPのパフォーマンスが改善されているのを反映
(2024年2月26日)OVR Metrics ToolがApp Labからの配布になったのを反映
(2024年1月13日)FindObjectsByTypeについて注記


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

UnityのQuest対応の概要

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

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

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

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

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

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

MetaのSDKが対応しているUnityのバージョンは Set Up Development Environmentのページで確認できます(日本語ページは情報が古い場合があるので注意してください。ページの右下で言語設定を切り替えられます)。2024年1月現在、Unity 2021.3以降が必要となっています。URPを使用する場合はUnity 2022.3 LTS以降にするのがおすすめです。

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

Questを開発者モードにする

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

なお、開発者モードにするには、Metaアカウントの2段階認証を有効にするか決済方法を設定する必要があります。

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

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

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

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

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

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

新規プロジェクトを「3D」テンプレート(Built-in Render Pipeline)または「3D(URP)」テンプレートで作成し、Build SettingsでAndroidプラットフォームに切り替えます。

Meta XR Core SDKをインポートする

Asset StoreでMetaのSDKパッケージ一式が公開されています。最低限必要なコアパッケージはMeta XR Core SDKになりますので、こちらをプロジェクトにインポートします。

インストールすると、以下の2つのダイアログが表示されます。

  • Metaに情報を送るかどうかという「Help improve the Oculus SDKs」ダイアログが出てくるのでお好みでどちらかのボタンをクリック
  • 新しいOVRPluginを認識しましたというダイアログが出るので「Restart Editor」をクリックしてUnityエディタを再起動

プロジェクトの推奨設定をする

Unityが再起動すると、右下に赤いドットのついたOculusのマークが表示されています。

これをクリックして「Project Setup Tool」を選択すると、以下のようなProject Settingsのページが開きます。このページで、UnityのXRプラグインの有効化や各種推奨設定を一括で自動的に行うことができます。ひとまずWindowsとAndroidのタブでそれぞれ「Fix All」をクリックして全部緑にしてください。

プロジェクトの設定を確認する

プロジェクトがどうなっているか簡単に確認してみましょう。

UnityのXRプラグインシステムであるXR Plug-in Managementが有効化され、Oculus XR Pluginがインストールされて、Project Settings > XR Plug-in Managementに下記チェックボックスが入っているはずです。これによりプロジェクトがVRモードで動作するようになります。

さらに一つ下のXR Plug-in Management > OculusのページにはVR関連の細かい設定が含まれていますので確認しておいてください。

また、Assets/Oculus/OculusProjectConfig.asset にはOculus固有の機能の設定があり、Assets/Plugins/Android/AndroidManifest.xml にはAndroidのマニフェストファイルが作成されています。

パッケージ名をつける

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

サンプルシーンをビルド・実行する

Package ManagerのMeta XR Core SDKのページの下のほうからサンプルシーンをインストールできます。

インストールしたら、試しにAssets/Samples/Meta XR Core SDK/(バージョン)/Sample Scenes/ControllerModelsシーンを開いてみてください。

Build Settingsを開き、開発者モードにしたQuestを接続してBuild and Runボタンを押し、適当な.apkファイル名を指定するとビルドと実機への転送が始まります。初回のビルドはPCの性能により3分から10分程度の時間がかかりますが、2回目以降はずっと短い時間で済みます。成功するとアプリが起動し、コントローラーのスティックで移動・回転できます。

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

  • Quest上で一度アプリをアンインストールしてBuild and Runし直すと上手くいく場合があります(別のPCでビルドした場合や、Package Nameが衝突した場合など)。
  • Project Setup Toolでプロジェクトの問題がレポートされるので確認します。
  • Window > Package ManagerでXR Plug-in ManagementパッケージとOculus XR Pluginパッケージのバージョンを確認して必要なら更新します。

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

ビルドして実機に転送したアプリは、Questに格納されていていつでも実行できます。場所が分かりにくいのですが、「アプリ(Apps)」メニューで「すべて(All)」を「提供元不明(Unknown Sources)」に変更すると出てきます。

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

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

WindowsにOculusソフトウェアをインストールしてMeta QuestをQuest Linkで接続すると、Unityのエディタ上でシーンをVRで再生できます。ビルドしなくても動作確認ができるため開発が楽になります。VRモードで再生するには、Project Settings > XR Plug-in ManagementのWindowsのタブでInitialize XR on StartupとOculusのチェックが有効になっていることを確認してください。

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

エディタ再生でフレームレートが出ない場合は、Gameウィンドウの右上のバーでPlay Maximizedを選択してウィンドウが最大化されるようにしてみてください。

Questのログ出力を見るには

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

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

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

MetaのSDKをアップデートするには

Package ManagerのIn Projectで個別のSDKをアップデートしてください。

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 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で下のようなウィンドウが開いて、必要な機能をクリックしていくだけでカメラリグを組み立てたり、プロジェクトの自動設定をしてくれたりするようになっています。最初のセットアップが簡単になるので試してみるといいでしょう。

Oculus Integrationから移行するには

Oculus Integartionは非推奨になり、Quest開発はMetaの新しいSDKパッケージを使用するようになりました。Meta XR Core SDKにOculus IntegrationのAssets/VRフォルダ相当が含まれているほか、これまでにOculus Integrationに入っていたものが個別のパッケージに分散しています。

新しいSDKパッケージに更新するには、Unityをいったん終了して(プラグインのDLLを解放するために必要です)、プロジェクトのバックアップを取るかバージョン管理した状態でAssets/Oculusフォルダを削除し、Unityを開き直して必要なパッケージをインストールしてください。

MetaのSDKパッケージ一覧

Asset StoreでMetaのSDKパッケージ一式が公開されていますが、個々のパッケージの内容について説明します(Meta XR Simulator関連とVoice SDK関連はいったん除いています。あとで個別の機能の項目に分ける予感)。

コアパッケージ

Meta XR Core SDK

最低限必要なパッケージです。従来のOculus IntegrationのVRフォルダ相当が含まれていて、パッケージのSamplesから基本的なサンプルシーンがインストールできます。

Interaction SDK関連のパッケージ

以下がInteraction SDK関連のパッケージで、Interaction SDKを使用する場合、基本的にはすべてインストールします。

Meta XR Interaction SDK

Interaction SDKのコアパッケージです。

Meta XR Interaction SDK OVR Integration

Meta XR Interaction SDKをQuestに対応させるパッケージです。たとえば、Meta XR Interaction SDKのHandsプレハブから派生するOVRHandsプレハブが含まれています。

Meta XR Interaction SDK OVR Samples

パッケージのSamplesに、単体の機能をデモするFeature Scenesと、複合的なデモのExample Scenesがあります。サイズが大きめです。

Avatars SDK関連のパッケージ

Meta Avatars SDK

Avatars SDKのコアパッケージです。パッケージのSamplesにサンプルシーンが含まれています。

Meta Avatars SDK Sample Assets

プリセットアバターのアセットが含まれています。インポートすると60~100MB超のzipファイルがAssets/Oculusフォルダに数個コピーされるので(中身は多数の.glbファイルです)、GitHubにpushする場合はLFSが必要になります。

その他のパッケージ

Meta XR Platform SDK

マッチメイキング、DLCやアプリ内課金、クラウドストレージ、実績等のMetaのプラットフォーム機能が入っています。

Meta MR Utility Kit

パススルーを使用したMRアプリ開発のためのScene API関連の機能が入っています。

Meta XR Audio SDK

3Dサウンド関連の機能が入っています。詳しくは、3Dサウンドを使用するにはを参照してください。

Meta XR Haptics SDK

波形ベースの高レベルなコントローラー振動機能が入っています。

Meta XR SDK Shared Assets

他のパッケージのサンプル等で使用される3Dモデルやテクスチャ、シェーダーその他が入っています。他のパッケージから参照されて自動的にインストールされます。

Meta XR All-in-One SDK

All-in-Oneという名前から全部入りのパッケージに見えますが、このパッケージ自体には中身がなく、他の一通りのパッケージへのDependenciesが入っています。

各パッケージのサンプルをインストールするには個別のパッケージをインストールする必要があるので、これをインストールする必要はないかも……?


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

プロジェクトの各種設定について、現在、Project Setup Tool(Unityのエディタ右下のOculusアイコンから開きます)を使用すると自動的に推奨設定にしてくれるようになっていますが、選択の余地があるものについて説明します。

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

レンダリングパイプラインはビルトインまたはUniversal Render Pipelineが使用できます

Unity 2021から2023あたりにかけて、URP 11以降でパフォーマンスが出ないという問題がUnityの公式フォーラム等で長らく報告されていましたが、該当IssueがUnity 2022.3.11と2023.1.14でFixedになっており、その結果概ね改善しているようだとの報告があります。URPを使用する場合はUnity 2022.3 LTSにして、URPパッケージのバージョンもできるだけ新しくするのがいいでしょう。

アンチエイリアス

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

OpenGL ESかVulkanか

グラフィックス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.

その後もしばらくOpenGL ESにしないと不安定だったりパフォーマンスが出なかったりする時期があったのですが、2024年1月現在、Vulkanにしたほうがよさそうです。Unity 2022.3でVulkanにしないとクラッシュが頻発する症状を確認しています。また、パススルー関連の機能やApplication SpaceWarp等の新しい機能を使用する場合もVulkanにする必要があります。

一方で、アセットによってはVulkan未対応なので要注意です。例えばAVPro VideoがVulkanで動きません。次のversion 3で対応予定とのことです。

IL2CPP

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

なお、MonoビルドはIL2CPPビルドと比べてCPU処理の負荷が数倍増えることがあります。また、カメラパススルー等、IL2CPPビルドにしないと動かない機能があるので注意してください。

VRのカメラ配置の基本

UnityのシーンをQuestのVRモードに対応するには、デフォルトで作成されているMain Cameraを削除して、代わりにPackages/Meta XR Core SDK/Prefabs/OVRCameraRigプレハブをシーンに配置します。プレハブに追加されているOVRCameraRig.csによって、OVRCameraRigの孫のCenterEyeAnchorがヘッドセットの動きに追従します。

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

なお、Unityのデフォルト状態だとSceneウィンドウに表示されるカメラアイコンが大きすぎて位置調整がしづらいと思います。Sceneウィンドウの右上のボタンをプルダウンして、3D Iconsのスライダーを左に動かしてみてください。

立って体験するアプリを作るには

OVRCameraRigプレハブをシーンの地面と同じ高さに配置して、OVR ManagerコンポーネントのTracking Origin TypeをFloor LevelまたはStageに変更します。

Floor Levelだとリセンターができる、Stageだとリセンターができないという違いがあります。Floor Levelにするとヘッドセットをはずしてかぶり直したときにリセンターがかかるので、ルームスケールやアリーナスケール等、立って歩きまわるアプリを開発する場合はStageにします(なお、Quest Linkでの実行ではStageにしてもヘッドセットの脱着でリセンターがかかります)。

adbコマンドについて

adb (Android Debug Bridge)はGoogleから提供されているAndroidデバイスの各種操作を行うためのコマンドラインツールです。Questでも使用できます。

adbコマンドは下記ページからダウンロードできるほか、UnityやMeta Quest Developer Hubに同梱されています。異なるバージョンのadbコマンドを同時に使用すると衝突して動かないことがあるので注意が必要です。adb kill-serverを実行すると動くようになる場合があります。

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

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

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

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

なお、Meta XR Core SDKのサンプルシーンのControllerModelsシーンに上記セットアップ済みのOVRCameraRigがあります。

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

TODO:要再確認

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

Meta XR Core SDKのHandsTestシーンに上記セットアップ済みの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プレハブをシーンにドロップするとコントローラーでポイントして操作できるようになります。

TODO:新しいSDKパッケージにUIHelpersが見当たらないのでどうするか確認

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

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

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

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

Meta XR Core SDKのSample 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ボタンを押したときの処理

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

Oculusボタンを押すとOVRManager.InputFocusLostイベントが、復帰するとOVRManager.InputFocusAcquiredイベントがトリガーされます。

ポーズ処理をするにはいろんな方法がありますが、一つの方法としてはTime.timeScaleをゼロにするというのがあります。

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

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

その他、Questのアプリケーションライフサイクルで送られてくるイベントの一覧が下記ページにあります。

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ではフルカラー表示です。

以下のような表示をする最低限必要な方法を説明します。

まず、空のシーンを作成し、OVRCameraRigプレハブと、その前方に適当なオブジェクト(ここではキューブ)を配置します。

アプリのパススルー機能を有効にするために、OVR ManagerのPassthrough SupportをRequiredまたはSupportedにして、Oculus > Tools > Update AndroidManifest.xmlを実行します。Assets/Plugins/Android/AndroidManifest.xmlが更新され、以下の行が追加されます(Supportedだとrequiredがfalseに、Requiredだとtrueになります)。

<uses-feature android:name="com.oculus.feature.PASSTHROUGH" android:required="false" />

さらに、OVR Managerの下のほうにあるEnable Passthroughをオンにします。

パススルー映像の表示レイヤーを追加します。OVRCameraRigにOVR Passthrough Layerコンポーネントを追加して、PlacementをUnderlayに変更します(パススルー映像を奥側に表示するという設定です)。

最後に、パススルー映像を消去してしまわないように、CenterEyeAnchorのカメラのClear FlagsをSolid Colorに、Backgroundのカラーを(0, 0, 0, 0)に設定します。

Build and Runでアプリを実行すると、上のスクリーンショットのような表示ができているはずです。

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

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

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

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

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

OVR ManagerのPassthrough SupportをRequiredまたはSupportedにした上で、下記コードでEnable Passthroughを実行時に切り替えます。

OVRManager.instance.isInsightPassthroughEnabled = true;

起動時にパススルー表示したくない場合はあらかじめオフにしておきます。なお、シーンを切り替えるとこの設定はオフに戻るため、シーン遷移後に再度オンにする必要があります。

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

TODO:今どこに入っているか確認

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

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

TODO: Meta MR Utility Kitを使用するように書き換える?

部屋のメッシュを取得する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のオブジェクトを遮蔽するには

パススルーカメラから取得する物理空間の深度情報を利用して、(完全ではありませんが)バーチャルなオブジェクトと現実のオブジェクトの前後関係を正しく表現できます。必要なパッケージおよびサンプルプロジェクトがGitHubで公開されています。

リポジトリのDepthAPI-BiRPフォルダ(ビルトインレンダーパイプライン版)とDepthAPI-URP(URP版)フォルダにサンプルプロジェクトが入っているので、まずこれをビルドして動作を確認してみましょう。

いくつかのサンプルシーンが含まれています。

OcclusionToggler

実行するとコントローラーのAボタンでオクルージョンのタイプ(オクルージョンなし、Hard Occlusion、Soft Occlusion)を切り替えます。Soft Occlusionだとソフトにくり抜かれますが、若干GPU負荷がかかるようです。

シーンにはオクルージョン処理のためにEnvironmentDepthOcclusionプレハブが配置され、3Dオブジェクトの表示にオクルージョンを適用する専用のシェーダー(Occlusion Standard等)が使用されています。

PerObjectOcclusion

オブジェクトごとにオクルージョンのタイプを変更するサンプルです。Occlusion Controllerコンポーネントでマテリアルのキーワードを変更しています。

HandRemoval

深度マップから手を除去して、代わりにハンドトラッキングを利用して手の部分をくり抜くサンプルです。UGUIの手前に現実の手を表示できていますが、ハンドトラッキングなのでどうしてもずれるのと、指の周囲の隙間から向こう側がはっきり見えるのでちょっと違和感があるかもしれません。

深度マップからの手の除去にはOculus XR PluginのUtil.SetEnvironmentDepthHandRemovalが呼び出されています。

SceneAPIPlacement

バーチャルなオブジェクトが現実の壁に近いときにオクルージョンが上手くいかないので、Scene APIで取得した部屋の形状もオクルージョンに用いるサンプルです。OVR Scene ManagerでOcclusion StandardシェーダーのVolume Prefabを配置してします。

パススルーにオクルージョン処理を追加する手順

パススルー表示にオクルージョン処理を追加する具体的な手順です。

まず、Unity 2022.3.1または2023.2で「パススルー機能を使うには」を参考にパススルー表示ができるようにします。

GitHubのリポジトリの下記URLからDepth APIパッケージをインストールします。

ビルトイン版:https://github.com/oculus-samples/Unity-DepthAPI.git?path=/Packages/com.meta.xr.depthapi
URP版:https://github.com/oculus-samples/Unity-DepthAPI.git?path=/Packages/com.meta.xr.depthapi.urp

Project Setup Toolを使用してプロジェクトの問題を修正します。特にグラフィックスAPIをVulkanにしてMultiviewレンダリングにする必要があります。

OVR ManagerのScene SupportをRequiredにしてOculus > Tools > Update AndroidManifest.xmlを実行します。

Depth APIパッケージに入っているEnvironmentDepthOcclusionプレハブをシーンに配置して、プレハブのOcclusion TypeをHard OcclusionまたはSoft Occlusionに変更します。

オブジェクトの描画時にオクルージョンを適用するために専用のシェーダーを使う必要があります。シェーダーのMeta/Depthの下に、Standardシェーダーをオクルージョンに対応したOcclusion Standard、Particles/Standard Unlitをオクルージョンに対応したOcclusion Particles Standard Unlitがあります。

任意のシェーダーにオクルージョン処理を追加する方法がこちらに載っています。頂点シェーダーとフラグメントシェーダーにオクルージョンのマクロを追加するか、Shader Graphの場合はOcclusionSubGraphノードをアルファにかける感じになります(要確認:ビルトインもShader Graphで行ける?)。

なお、深度情報の取得にはMesh APIのメッシュデータとの統合(depth fusion)が行われるため、スペースを認識させていないと不正確になるものの、スペースのデータなしで使うこともできなくはないとのことです。

空間アンカーを使うには

空間アンカーはセッション間で保持される物理空間の位置です。パススルー機能と組み合わせることで、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以上に上げるのがおすすめです(現在、MetaのSDK自体もUnity 2021.3以降が必須要件になっています)。

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

Monoビルドにする

Metaにアプリを提出する場合は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を使用している場合は、実行開始時にフィルターが自動で設定されないため自分で設定する必要があります。

パフォーマンス最適化

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

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

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

QuestのCPU・GPUについて

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

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

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はSnapdragon 835に統合されている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の各種メトリクスをヘッドセット内でオーバーレイ表示できます。アプリがどんなシチュエーションで重くなるかをヘッドセットをかぶったままで簡易的に確認できます。

OVR Metrics ToolはApp Labからインストールできます。起動すると設定画面が出てくるので、「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 のプロファイラー

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のレンダリングパイプラインの解説があります。

モバイルGPUで使用されているタイルベースレンダリングについて簡潔に説明されているページです。

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

QualcommのサイトにSnapdragonのゲーム開発者向けガイドがあり、左のタブのAdreno GPUのところにベストプラクティスが掲載されています。シェーダーの最適化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の仕組みとしては、タイルベースレンダリングのタイルごとに解像度を変えているようです。詳細の解説がこちらにあります。

なお、URPでもFoveated Renderingは使用できますが、Custom Render Featureと併用できない制限があります。設定を有効にすとOVR Metrics Toolの表示は変わるのですが、描画に反映されずGPU使用率も減りません(URP 13で対処されている?)。

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にします(ただし、Project Setup ToolではデフォルトでETCに設定されるようです)。

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イベントが飛んできます。

Application SpaceWarpについて

Application SpaceWarpは、(上手くいけば)見た目をあまり損なわずにフレームレートを半分にできる最適化機能です。Unityでたとえば36fpsの映像フレームを生成すると、Questのコンポジターが中間フレームを補間して72fpsの映像を生成します。半分のフレームレートでレンダリングすればいいので、CPUとGPUの負荷が半分近く減少します。

Application SpaceWarpにはURPが必要で、フレーム内のピクセルの移動方向を格納した特殊な(Unity標準のものとは異なる)モーションベクターバッファを描画する必要があります。独自のシェーダーでApplication SpaceWarpの恩恵を得るには、シェーダーにモーションベクターバッファの描画パスを追加する必要があります。

フレームレートが下がる結果コントローラーの入力遅延は大きくなったり、描画内容(HUDやメッシュアニメーション、パーティクル等)によってはフレーム補間がまったく効かなかったりするので、導入にあたってはトレードオフの検討が必要です。

使用するには、大まかに、グラフィックスAPIをVukanに、Oculus XR PluginのApplication SpaceWarpのチェックをオンにし(さらにLate Latchingオンを推奨とのことです)、MetaがUnity公式からフォークしたURPとShader Graphのパッケージをインストールします。OVRManager.SetSpaceWarpで実行中にオン・オフでき、アプリ全体でオンにしたければ最初にオンにします。

Application SpaceWarpのサンプルを動かす

GitHubにApplication SpaceWarp対応のScriptable Render Pipelineリポジトリがあります。Unityの各バージョンに対応したoculus-app-spacewarpという名前のブランチがありますので、必要なバージョンを調べてcloneします。

git clone -b 2021.3/oculus-app-spacewarp https://github.com/Oculus-VR/Unity-Graphics/

TestProjects/OculusAppSpaceWarpSimpleフォルダ(2022.3ブランチはOculusAppSpaceWarpSampleフォルダ)にUnityのプロジェクトがあるので、Androidプラットフォームで開きます。プロジェクトがUnity 5.0より前のような警告ダイアログが出てくるかもしれませんが無視します。

Meta XR Core SDKをインポート、Assets/ScenePackage.unitypackageをダブルクリックしてScenePackage/SpaceWarpScene.unityを開きます。

ビルドしてBボタンでApplication SpaceWarpをオン・オフします。有効になっていれば、Android Logcatの毎秒の出力にASWの項目が出てきます。また、OVR Metrics ToolのASW FPSの項目がゼロから変動します。たとえばリフレッシュレート72HzでApplication SpaceWarpが効いていればFPS: 36、ASW FPS: 72と表示されます。

Application SpaceWarpの仕組み

フレームを補間するために、アプリケーションはシェーダーに追加されたMotionVectorsパスを使用して低解像度(Quest 2では368x400)のモーションベクターバッファを生成します。バッファはOpenXRのXR_FB_space_warp拡張を通してコンポジターにサブミットされます。

モーションベクターバッファには、各ピクセルの前のフレームと現在のフレームのNDC(正規化デバイス座標)の差が3Dベクトルで入ります。ピクセル深度も格納されます。

コンポジターは、モーションベクターバッファを利用してフレームの各ピクセルが次のフレームのどこにあるかを推測します。また、Questの通常のTimeWarpではカメラの回転しか補正しませんが、Application SpaceWarpではモーションベクターバッファに格納されたピクセルの深度によりカメラの移動についても補正します(Positional TimeWarp)。

なお、Unityの標準機能にもモーションベクターバッファがありますが、負荷低減のために低解像度のバッファを生成するのと、深度バッファを生成するために専用のモーションベクターパスが使用されています。

このコミットがシェーダーで専用のモーションベクターバッファを描画するための主要な変更箇所として紹介されています。とりわけ、OculusMotionVectorCore.hlslの下記フラグメントシェーダーが根幹部分になります。

half4 frag(Varyings i) : SV_Target
{
    float3 screenPos = i.curPositionCS.xyz / i.curPositionCS.w;
    float3 screenPosPrev = i.prevPositionCS.xyz / i.prevPositionCS.w;
    half4 color = (1);
    color.xyz = screenPos - screenPosPrev;
    return color;
}

Application SpaceWarpの弱点

上記の原理上、描画内容によってはフレーム補間が上手くいきません。下記リポジトリのREADMEにApplication SpaceWarpによって発生する典型的なアーティファクトのサンプル動画があります。

UIの描画

ゲームのUIやHUDをオーバーレイ表示すると、モーションベクターバッファにより乱れが発生します。Application SpaceWarpの影響を受けないOVROverlayで描画するか、3D空間内にUIを配置する必要があります。

半透明レンダリング

モーションベクターバッファには重なっている2つ以上のピクセルの動きを格納できないため、半透明に弱いです。遠方のオブジェクトやアニメーションが速いエフェクト等は問題ないものの、近距離を高速で動くオブジェクトは要注意とのことです。たとえばバーチャルなコントローラーに透過するメニューパネルがついていたりするとまずそうです。

その他

高速回転するオブジェクト、複雑な頂点アニメーション、人工的なテクスチャパターン等に弱いです。また、パーティクルエフェクトなどはそもそもフレーム補間されようがないので半分のフレームレートで描画されてしまいます。

これらについて、描画の仕方を変える、モーションベクターバッファの描き方を変える、状況に応じてApplication SpaceWarpをオフにする等の対策を考える必要があります。

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

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

3Dサウンドを使用するには

Meta XR Audio SDKパッケージに3D空間音響のプラグインが含まれており、QuestとPC両方で動作します。

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

また、音が部屋の壁で跳ね返ることによる残響音(初期反射音およびリバーブ)や、Near-field Rendering(距離1メートル未満の音源について音波の回折効果でリアルな表現にする)の効果も実装されています(技術的な解説ページ)。

セットアップ方法

Meta XR Audio SDKをインポートすると、Project Settings > AudioのSpatializer PluginとAmbisonic Decoder PluginにMeta XR Audioが設定されます。必要に応じてDSP Buffer Sizeを調整します(Best latencyにするといいとのことです)。

プロジェクト内にAssets/Resources/MetaXRAudioSettingsが作成され、Voice Limit(同時発音数)の設定があるのでパフォーマンス次第で調整します。

パッケージのSamplesからExample Scenesがインストールできます。なおサンプルシーンはどれもVRモードではなく、WASDキー+マウスで移動するようになっています。

音の定位感を得るには

Audio SourceのついたオブジェクトにMeta XR Audio Sourceコンポーネントを追加することで3Dサウンドの効果が有効になります。3Dサウンドに使用できる音源はモノラル音源のみです。Reverb Send Dbを上げるとリバーブがかかります。

さらにMeta XR Audio Source Experimental Featuresコンポーネントを追加すると以下の設定ができるようになります。

Volumetric Radius

音源の大きさを設定します。技術的な解説ページがあります。

Directivity Intensity / Directivity Pattern

音に指向性を持たせます。人の話し声であればDirectivity PatternをHuman Voiceに変更するとよさそうです。Example Scenesに含まれているSourceDirectivelyシーンでは3人の指向性のある声による会話が聞こえてくるのですが、だいぶ効果的で、ちょっとびっくりするのでおすすめです。

音の向きを変えるにはゲームオブジェクトを回転させればオーケーです。

Hrtf Intensity

HRTFのかかり具合を調整します。クリエイティブな理由で気になったら下げるようにとのことです。

空間の残響音を得るには

部屋の形状をシミュレートして初期反射音とリバーブ(残響音)の効果を追加できます。Example Scenesに含まれているRoomAcousticsシーンがサンプルシーンになります。

必要な設定としては、Audio MixerにMeta XR Audio Reflectionを挿して、Meta XR Audio Room Acoustic Propertiesコンポーネントがついたオブジェクトを配置し、このコンポーネントで部屋のサイズとそれぞれの壁の材質を設定します。複数の部屋を移動したい場合はプロパティを更新すると反映されます。

Ambisonicsを再生するには

360度音響のAmbisonicsの音源を再生できます(一次AmbisonicesのAmbiXファイル)。再生するにはProject Settings > AudioのAmbisonic Decoder PluginにMeta XR Audioが設定されていることを確認した上で、AudioSourceのAudioClipに設定するだけです。AudioSourceまたはAudioListenerのゲームオブジェクトを回転すると音源が回転します。

Example ScenesにサンプルのAmbisonicsシーンが含まれています。

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

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ボタンを押したときの処理あたりがおそらく引っかかりやすいポイントだと思います。

Target API Levelを32にする

2023年6月以降、MetaのダッシュボードにアプリをアップロードするにはProject Settings > Player > Target API Levelを32にする必要があります。が、32にするとビルドが通らないことがあります。「Unity 2022でのトラブル」を参照してください。

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

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をインポートすると、Oculus/LipSyncやOculus/AvatarによってUnityがビルド後のAndroidManifest.xmlにRECORD_AUDIO等のパーミッションを自動的に付与してしまい、マイクを使用しないアプリなのにマイク権限があると審査でリジェクトされるということがありました。

TODO: 今OVRLipSyncがどのパッケージに入っているのか確認

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

マルチプレイヤーのゲーム等で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カードを使用すると改善される場合があります。

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

どうしても安定しない場合、Air Linkを試してみるのもおすすめです(Wi-Fi 6Eルーターを使うのがよさそうです)。

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は屋外で使用できるの?

Quest 1の場合、おそらくコントローラーの赤外線LEDが太陽光にかき消されるため、強い晴天下ではコントローラーの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開発メモトップへ