Meta QuestアプリでRemote AddressablesをJSONから切り替える構成
Meta Quest 3向けUnityアプリでRemote AddressablesをHTTP経由で取得し、設定JSONの変更だけで実行時にPrefabを切り替える構成と、Catalog更新、AssetBundleキャッシュの問題への対処を整理します。
Meta Quest 3にビルドしたUnityアプリから、PC上のAddressablesアセットをHTTP経由で取得し、実行時にPrefabを切り替えられるかを検証した。
最終的に確認したかったのは、Questアプリを毎回再ビルドせずに、PC側またはWeb管理画面側でシナリオ設定やアセットを更新し、VR空間の表示内容を切り替えられる構成である。
この記事では、固定AddressのPrefab表示から始めて、設定JSONによるPrefab切り替え、Catalog更新、AssetBundleキャッシュの問題までを整理する。
目的
今回の検証では、次の構成を目指した。
Web管理画面またはPC側設定 -> 設定JSONファイルを更新 -> 必要に応じてAddressables Bundleを更新 -> Questアプリが起動時に設定JSONファイルを取得 -> JSON内のAddress名をもとにPrefabをロード -> VR空間に表示最小検証では、PC上に置いた検証用PrefabをQuestアプリからロードした。次に、JSONの値を変更するだけで、表示するPrefabを切り替えられるかを確認した。
全体像
検証環境は次の通り。
Unity 6000.3.13f1
実行端末 Meta Quest 3
PC側 Unity Editor実行環境 Addressablesビルド環境 Python簡易HTTPサーバー
通信方式 QuestからPCへのHTTP通信PC側では、AddressablesのRemote Build成果物と設定JSONを同じHTTPサーバー配下に置いた。
[配信ルート]/ Android/ [Catalogファイル名].hash [Catalogファイル名].bin [Bundleファイル名].bundle Config/ [設定JSONファイル].jsonPythonの簡易HTTPサーバーは [配信ルート] 直下で起動した。
cd [配信ルートのパス]python -m http.server [ポート番号]この場合、Quest側からは次のURLにアクセスする。
http://[IPアドレス]:[ポート番号]/Config/[設定JSONファイル名].jsonhttp://[IPアドレス]:[ポート番号]/Android/[Catalogファイル名].hashhttp://[IPアドレス]:[ポート番号]/Android/[Catalogファイル名].binhttp://[IPアドレス]:[ポート番号]/Android/[Bundleファイル名].bundle[配信ルート]/Android 直下でHTTPサーバーを起動する場合は、Load Pathが変わる。今回の整理では [配信ルート] 直下で起動し、Load Pathを次の形にしている。
http://[IPアドレス]:[ポート番号]/AndroidAddressablesの設定
AddressablesにはRemote用のGroupを作成した。
Group名 [Remote Group名]
登録したAddress例 [Address名_標準] [Address名_A] [Address名_B] [Address名_C][Remote Group名] の重要設定は次の通り。
Build Path [配信ルート]/Android
Load Path http://[IPアドレス]:[ポート番号]/Android
Bundle Naming Mode Append HashAddressable Asset Settings側では、Remote Catalogを有効にした。
Build Remote Catalog ON
Remote Catalog Build Path [配信ルート]/Android
Remote Catalog Load Path http://[IPアドレス]:[ポート番号]/Android特に重要だったのは、Catalog側だけでなくRemote Group側のLoad PathもHTTP URLにすることだった。
正しい状態 [Remote Group名] Load Path = http://[IPアドレス]:[ポート番号]/Android
不正な状態 [Remote Group名] Load Path = <undefined>Group側のLoad Pathが未定義だと、Catalog設定が正しくても、Quest側はBundleを正しいHTTP URLから取得できない。
HTTP通信の許可
QuestブラウザからPCサーバーにアクセスできても、Unityアプリ内でHTTP通信が許可されていなければRemote Addressablesは失敗する。
実機ログには次のエラーが出ていた。
Non-secure network connections disabled in Player SettingsInsecure connection not allowedUnityWebRequest result : ConnectionError : Insecure connection not allowedUnityのPlayer SettingsでHTTP通信を許可した。
Edit Project Settings Player Android Other Settings Configuration Allow downloads over HTTPローカルLANでの検証では、次の設定にした。
Allow downloads over HTTP Always allowed設定JSONでPrefabを切り替える
Quest側が最初に取得する設定ファイルとして設定JSONファイルを用意した。
{ "scenario_id": "[シナリオID]", "spawn_asset": "[Address名]", "spawn_position": { "x": 0, "y": 1.2, "z": 2 }}spawn_asset を変更すると、Quest側でロードするAddressablesアセットが変わる。
[Address名_標準][Address名_A][Address名_B][Address名_C]Quest側の起動処理は次の流れにした。
1. [設定JSONファイル名] をHTTPで取得2. JSONをパース3. spawn_assetを取得4. Addressables Catalogの更新を確認5. Catalog更新があればUpdateCatalogsを実行6. AddressのLocation情報をログ出力7. Download sizeを確認8. DownloadDependenciesAsyncを実行9. LoadAssetAsync<GameObject>でPrefabをロード10. 指定位置にInstantiate11. 失敗時は [Fallback Prefab名] を表示ログには、少なくとも次を出すようにした。
Location countLocation PrimaryKeyLocation InternalIdLocation ProviderIdLocation ResourceTypeDependency countDependency PrimaryKeyDependency InternalIdDependency ProviderIdDependency ResourceTypeDownload sizeDownloadDependencies resultLoadAssetAsync resultこれにより、Addressが見つからないのか、Catalogが古いのか、Bundleが取得できていないのか、Prefabロードだけが失敗しているのかを切り分けられる。
最初に成功したこと
最初に、固定AddressでPrefabをロードした。
[Address名_標準]この段階で確認できたことは次の通り。
QuestからPCへのHTTP通信ができるAddressables Catalogが読めるRemote Bundleを取得できるBundle内のPrefabをGameObjectとしてロードできるQuest上にPrefabを表示できる成功までの流れは次の通り。
1. PC側でAddressablesを Android 向けにビルド2. [配信ルート]/Android にBundleとCatalogを出力3. PCでpython -m http.server [ポート番号] を起動4. Questブラウザから http://[IPアドレス]:[ポート番号]/Android にアクセス確認5. Unity Player SettingsでHTTP通信を許可6. Questにアプリを再ビルドして再インストール7. Questアプリ内で [Address名_標準] をロード8. PrefabがVR空間に表示されたここで注意すべきなのは、Unity Editor上で表示されることは実機成功を意味しない点である。EditorではPlay Mode Scriptの設定によって、Remote BundleではなくAsset Databaseから直接読めてしまう場合がある。
実機検証では、対象プラットフォーム向けにAddressables Buildしているか、Questアプリを再ビルドしているか、実機ログでエラーを見ているかを確認する必要がある。
はまった点
adbにPATHが通っていない
Quest実機ログを確認しようとして次を実行した。
adb logcat -s Unityしかし、adb がコマンドとして認識されなかった。
原因は adb.exe にPATHが通っていなかったこと。Unityに同梱されているADBを直接使うことで解決した。
cd "[Unityエディタのインストールパス]\Editor\Data\PlaybackEngines\AndroidPlayer\SDK\platform-tools".\adb.exe devices.\adb.exe logcat -s UnityQuest側に古いCatalogまたはキャッシュが残る
Remote Groupの設定を直しても、既にQuestへインストール済みのアプリは古いCatalogや古いAddressables状態を持っていた。
そのため、正しいRemote Load Pathを持つ状態で一度Questアプリを再ビルドし、再インストールする必要があった。
実施したことは次の通り。
1. Quest側のアプリをアンインストール2. Addressablesを正しい設定でNew Build3. Questアプリを再ビルド4. Questへ再インストール5. [Address名_A] をロードこの結果、対象Prefabが表示された。
成功時のログは次の状態だった。
Location PrimaryKey [Address名_A]
Location InternalId [PrefabファイルのUnity内パス]
Location ProviderId BundledAssetProvider
Dependency InternalId http://[IPアドレス]:[ポート番号]/Android/[Bundleファイル名].bundle
Download size [ダウンロードサイズ]
DownloadDependencies succeeded
LoadAssetAsync succeeded
Spawn [Address名_A]サーバー側にも対象BundleへのGETが出た。
"GET /Android/[Bundleファイル名].bundle HTTP/1.1" 200アプリ再ビルドなしの新規Prefab追加で失敗する
次に、Questアプリは再ビルドせず、[Address名_C] を追加する検証を行った。
手順は次の通り。
1. Questアプリはそのまま2. [Prefab名_C].prefabを作成3. [Remote Group名] に追加4. Addressを [Address名_C] に設定5. AddressablesでUpdate a Previous Buildを実行6. "spawn_asset" を [Address名_C] に変更7. Questアプリを再起動このとき、[Address名_C] はCatalog上では見えていた。
Location PrimaryKey [Address名_C]
Location InternalId [PrefabファイルのUnity内パス]
Location ProviderId BundledAssetProvider
Dependency InternalId http://[IPアドレス]:[ポート番号]/Android/[Bundleファイル名].bundle
Download size [ダウンロードサイズ]
DownloadDependencies succeededしかし、最後にPrefabロードで失敗した。
Unable to load asset of type UnityEngine.GameObject from location [PrefabファイルのUnity内パス].別の新規Prefabを作成して再検証しても同じだった。そのため、Prefab個体の破損ではなく、デバイス側のAssetBundleキャッシュ、Catalog更新、Bundle ID競合の可能性が高いと判断した。
安定化のために効いた設定
最終的に有効だった対策は次の3つ。
1. Unique Bundle IDsをONにする2. UpdateCatalogsでautoCleanBundleCacheを有効化する3. 検証用にClearDependencyCacheAsyncを追加するUnique Bundle IDs
Addressables Asset Settingsで次を有効にした。
Addressables Asset Settings Build Unique Bundle IDs ON目的は、Catalog更新後に古いBundleと新しいBundleの内部ID競合を避けること。
ただし、これは常に無条件で有効にすべき設定ではない。Unityのドキュメントでも、実行中にCatalogを更新して既にロード済みのBundleと競合する場合には有効だが、ビルド時間や更新サイズが増える可能性があると説明されている。
今回の検証では、アプリ起動後または再起動時にRemote更新を扱うため、有効化した。
UpdateCatalogsでBundle Cacheを整理する
Catalog更新時に、不要になったBundleキャッシュを整理するようにした。
変更前のイメージ。
Addressables.UpdateCatalogs(catalogs, false);変更後のイメージ。
Addressables.UpdateCatalogs( true, catalogs, false);第1引数の true により、Catalog更新後に参照されなくなったBundle Cacheを削除する。
ClearDependencyCacheAsync
検証用として、対象Addressの依存Bundleキャッシュを明示的に削除してからダウンロードする処理を入れた。
Catalog更新 -> Location確認 -> Download size確認 -> ClearDependencyCacheAsync -> DownloadDependenciesAsync -> LoadAssetAsyncこれは、古いBundleキャッシュを使って失敗しているのかを切り分けるための処理である。本番では毎回無条件に呼ぶのではなく、Catalog更新時や復旧処理時など、必要な場面に限定する設計が望ましい。
New BuildとUpdate a Previous Buildの使い分け
初期構築やAddressables構成を作り直す場合は、New Buildを使う。
Addressables Groups Build New BuildQuestアプリを再ビルドせず、Remote側だけを更新する検証では、Update a Previous Buildを使う。
Addressables Groups Build Update a Previous Build判断基準は次の通り。
アプリも再ビルドする New Build
アプリは再ビルドしない Update a Previous Build最終結果
最終的に、アプリを毎回アンインストールしなくても、Addressables更新とJSON変更によるPrefab切り替えが成功した。
確認できた流れは次の通り。
Questアプリを一度正しい設定でビルドする -> PC側でAddressablesを更新する -> [設定JSONファイル名] を書き換える -> Questアプリを再起動する -> 新しいPrefabが表示されるまとめ
Remote Addressablesを使えば、Questアプリを毎回再ビルドせずに、PC側のアセット更新とJSON変更だけで表示Prefabを切り替える構成は実現できる。
ただし、安定運用には次が重要になる。
Remote GroupのLoad Pathを正しく設定するBuild Remote Catalogを有効にするUnique Bundle IDsの必要性を判断するUpdateCatalogs時にBundle Cacheを整理する必要に応じてClearDependencyCacheAsyncを使うLocation、Dependency、Download sizeログを出すサーバー側GETログを確認する特に、アンインストールすると成功するが再起動だけでは失敗する場合は、アプリロジックだけでなく、AddressablesのCatalog、Bundle、Cacheの整合性を疑うべきである。
参考
- Unity Addressables Content update builds: https://docs.unity.cn/Packages/com.unity.addressables@1.19/manual/ContentUpdateWorkflow.html
- Unity Addressables UpdateCatalogs API: https://docs.unity.cn/Packages/com.unity.addressables@1.19/api/UnityEngine.AddressableAssets.Addressables.UpdateCatalogs.html
- Unity Addressable Asset Settings: https://docs.unity3d.com/ja/Packages/com.unity.addressables@1.20/manual/AddressableAssetSettings.html
用語リスト
- Addressables: Unityのアセット管理機能。Addressという文字列キーでPrefabや音声などをロードできる。
- AssetBundle: Unityのアセットを実行時に読み込める形式でまとめたファイル。
- Catalog: AddressablesがAddressと実際のBundleやアセット位置を対応づけるための管理ファイル。
- Remote Group: AddressablesのGroupのうち、アプリ本体ではなく外部サーバーから取得するアセットを置くGroup。
- Load Path: 実行時にQuestアプリがBundleやCatalogを取りに行くURL。
- Prefab: Unity上で再利用できるGameObjectのテンプレート。
- Instantiate: Prefabから実際のGameObjectをシーン上に生成する処理。
- Fallback Prefab: ロード失敗時に代わりに表示するPrefab。
- Bundle Cache: 一度取得したAssetBundleを端末側に保存して再利用する仕組み。