UnrealEngineのPC VRアプリをMetaQuest3で動かしてみる

UnrealEningeのPC VRアプリをMeta Quest3で動かせるようにしました。
ここではその内容について記録しておきます。
ひとまず動かすようにしたまででプラグインの完全な対応やパフォーマンス最適化などは実施していません。

# PC VRアプリ

PC VRアプリについては以下のようなものになります。

* WindowsのUE5.3.2でWindows用に開発
* OpenXR利用(MetaXRプラグイン不使用)
* EpicOnlineService(EOS)によるマルチプレイ
* Blueprintを多用

# Quest3対応でしたこと

## Quest3用の設定

### 必要なAndroid関係のインストール

https://dev.epicgames.com/documentation/ja-jp/unreal-engine/android-development-requirements-for-unreal-engine?application_version=5.3

上記のサイトを参考にして以下のバージョンを指定してインストール

* AndroidStudio：Framingo 2022.2.1
([こちら](https://developer.android.com/studio/archive)から取得、すでに他の開発で入手済みだったのでzipで落として任意の場所に回答して複数バージョンを併用)
Android SDK Locationはc:\UEAndroid\Androidに設定
* SDK：Android-33
* NDK：25.1.8937393
[こちら](https://groups.google.com/g/android-ndk-announce/c/Cr2VhbQAAl8?pli=1)によるとNDK r25b = 25.1.8937393らしい
* BuildTool：33.0.1
* CommandLineTool：19.0(latest)

### プロジェクトの設定

[Platforms] > [Android]　> [APK Packaging]で[Project is not configured for the Android platform]となっているところを[Configure Now]実施、[Accept SDK License]実施

[Platforms] > [Android] > [APK Packaging]で以下のように設定

* Android Package Name:適当に設定
* Minimum SDK Version：30
* Target SDK Version：33

[Platforms] > [Android] > [Distribution Signing]で以下のように設定

* 用意してあったキーストアファイル用に一通りの項目を設定
キーストアファイルはプロジェクトのBuild/Androidに配置

[Platofrms] > [Android] > [Google Play Services]で[Project is not configured for the Google Play services]となっているところを[Configure Now]実施

[Platofrms] > [Android] > [Google Play Services]で以下のように設定

* [Enable Google Play Support：チェック外す

Engine/Extras/Android/SetupAndroid.batを実施し、[Platforms] > [Android SDK]で以下のように設定

* Location of Android SDK:c:/UEAndroid/Android
* Location of Android NDK:c:/UEAndroid/Android/ndk/25.1.8937393
* Location of JAVA:c:/UEAndroid/android-studio-2022.2.1.18-windows/android-studio/jbr
* SDK API Level:android-33
* NDK API Level:android-30

## パッケージング

### プラグイン対応

パッケージングでエラーが出ているAndroid対応していないプラグインはプロジェクトのPluginsフォルダに配置して以下のようにして対応させて動くようにしました。

```json:uplugin
"Modules": [
  {
      ...
      "WhitelistPlatforms": [
          "Win64",
          "Android"
     ]
  }
]
```

ビルドでエラーが出るが出る部分については以下のようにしてWindowsだけで動作するようにしました。
基本的にWindowsのヘッダや特有の型を使っている部分だけを限定的に囲むのと、特有の型を引数や返り値にしている部分はその関数丸ごと囲んで無効化しました。Blueprintに提供しているような関数などは残すようにようにしました。

```cpp:c++ファイル
#if PLATFORM_WINDOWS
...
#endif
```

### はまったところ

#### Android系のバージョン不一致

Android関係のバージョンをUnrealEngineの要求するバージョンに合わせていないと、turnkeyが起動しようとして失敗してエラーになっていました。

#### ライセンスエラー

以下のようなエラーが出てパッケージングに失敗しました。
プロジェクト設定の[Platforms] > [Android]で[accept sdk license]を押しても特に反応がない状態でした。

```text
UATHelper: Packaging (Android (ASTC)): Directory doesn't exist c:/UEAndroid/Android/platforms/android-33/licenses
UATHelper: Packaging (Android (ASTC)): Android SDK license file not found.  Please agree to license in Android project settings in the editor
```

プロジェクト設定の[Platforms] > [Android SDK] > [Location of Android SDK]が間違っていてライセンスの確認ができていませんでした。
Unreal Editorのデバッグをしたところ、[Location of Android SDK]の値がANDROID_HOME環境変数として設定され、WindowsのANDROID_HOME環境変数の値は無視されているみたいでした。

#### 署名設定のエラー

以下のエラーがパッケージングに失敗しました。

```text
UATHelper: Packaging (Android (ASTC)): Fixing minSdkVersion; NDK level is 30 which is above minSdkVersion 23.
UATHelper: Packaging (Android (ASTC)): DistributionSigning settings are not all set. Check the DistributionSettings section in the Android tab of Project Settings
```

プロジェクト設定で[Platforms] > [Android] > [Distribution Signing]を設定したら解消されました。

#### The SDK for Android is not installed properlyが出てエラーになる

以下のようにThe SDK for Android is not installed properlyというエラーが出てパッケージングが失敗しました。
Engine/Extras/Android/SetupAndroid.bat
を実行すると解消されました。

https://forums.unrealengine.com/t/android-packaging-problem-the-sdk-for-android-is-not-installed-properly/721125/1

#### プラグイン修正時のエラー

プラグインを修正してパッケージングすると以下のエラーで失敗しました。

```text
UATHelper: Packaging (Android (ASTC)): cmd.exe failed with args /c "...\Intermediate\Android\arm64\gradle\rungradle.bat" :app:assembleDebug
(see ...\Engine\Programs\AutomationTool\Saved\Logs\Log.txt for full exception trace)
```

Log.txtでは以下のように出ていました。
原因がわからなかったのですがプロジェクト設定で[Packaging] > [Full Rebuild]をチェックしてパッケージングすると解消されました。

```text:...\Engine\Programs\AutomationTool\Saved\Logs\Log.txt
==============================================================================
cmd.exe failed with args /c "...\Intermediate\Android\arm64\gradle\rungradle.bat" :app:assembleDebug
(see ...\Engine\Programs\AutomationTool\Saved\Logs\Log.txt for full exception trace)

BuildException: cmd.exe failed with args /c "...\Intermediate\Android\arm64\gradle\rungradle.bat" :app:assembleDebug
   at UnrealBuildTool.UEDeployAndroid.RunCommandLineProgramWithExceptionAndFiltering(String WorkingDirectory, String Command, String Params, ILogger Logger, String OverrideDesc, Boolean bUseShellExecute) in ...\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs:line 1845
   at UnrealBuildTool.UEDeployAndroid.MakeApk(AndroidToolChain ToolChain, String ProjectName, TargetType InTargetType, String ProjectDirectory, String OutputPath, String EngineDirectory, Boolean bForDistribution, String CookFlavor, UnrealTargetConfiguration Configuration, Boolean bMakeSeparateApks, Boolean bIncrementalPackage, Boolean bDisallowPackagingDataInApk, Boolean bDisallowExternalFilesDir, Boolean bSkipGradleBuild) in ...\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs:line 4856
   at UnrealBuildTool.UEDeployAndroid.PrepForUATPackageOrDeploy(FileReference ProjectFile, String ProjectName, DirectoryReference ProjectDirectory, String ExecutablePath, String EngineDirectory, Boolean bForDistribution, String CookFlavor, UnrealTargetConfiguration Configuration, Boolean bIsDataDeploy, Boolean bSkipGradleBuild) in ...\Engine\Source\Programs\UnrealBuildTool\Platform\Android\UEDeployAndroid.cs:line 5455
   at AndroidPlatform.Package(ProjectParams Params, DeploymentContext SC, Int32 WorkingCL) in ...\Engine\Source\Programs\AutomationTool\Android\AndroidPlatform.Automation.cs:line 0
   at AutomationScripts.Project.Package(ProjectParams Params, Int32 WorkingCL) in ...\Engine\Source\Programs\AutomationTool\Scripts\PackageCommand.Automation.cs:line 49
   at BuildCookRun.DoBuildCookRun(ProjectParams Params) in ...\Engine\Source\Programs\AutomationTool\Scripts\BuildCookRun.Automation.cs:line 237
   at BuildCookRun.ExecuteBuild() in ...\Engine\Source\Programs\AutomationTool\Scripts\BuildCookRun.Automation.cs:line 43
   at AutomationTool.BuildCommand.Execute() in ...\Engine\Source\Programs\AutomationTool\AutomationUtils\BuildCommand.cs:line 345
   at AutomationTool.BuildCommand.ExecuteAsync() in ...\Engine\Source\Programs\AutomationTool\AutomationUtils\BuildCommand.cs:line 354
   at AutomationTool.Automation.ExecuteAsync(List`1 CommandsToExecute, Dictionary`2 Commands) in ...\Engine\Source\Programs\AutomationTool\AutomationUtils\Automation.cs:line 265
   at AutomationTool.Automation.ProcessAsync(ParsedCommandLine AutomationToolCommandLine, StartupTraceListener StartupListener, HashSet`1 ScriptModuleAssemblies) in ...\Engine\Source\Programs\AutomationTool\AutomationUtils\Automation.cs:line 164
==============================================================================
```

## Quest3での動作

### アプリの変更

VRアプリではStart in VRをオフにして最初に接続のためのUIをビューポートに表示し、接続後にEnable HMDでVRモードにしていました。
このため、Quest3では画面が真っ暗なままだったために、Enable HMDを実施し接続のためのUIを3D空間上に配置するように変更しました。
いちおう動作できるようになったらMetaHorizonダッシュボードでアプリを作ってアップロードしました。

### はまったところ

#### shippingからdevelopmentビルドに変更してログが出ない

shippingビルドからdevelopmentビルドに変えただけだと反映されずにshippingビルドのパッケージが出続けていました。
プロジェクトの再起動が必要でした。

#### shippingビルドでログを出す

shippingだけでクラッシュする問題を調査しようとしてログを出すようにしました。
ログを出す方法は以下の記事を参照してください。
プロジェクトのTarget.csの変更に加えてEngineコードの修正が必要だったのでハマりました。

https://qiita.com/mechamogera/items/7f625608293b399b803b

#### Blueprintで利用しているプラグインが入っていないのでクラッシュする

ログを見て問題となっているプラグインを特定し、パッケージングで行っていたように対象のプラグインをAndroid対応させました。

#### shippingビルドだけでクラッシュして起動しない

以下のようなログが出てクラッシュしていました。
developmentビルドでは問題なく起動していました。

```text
DEBUG
Abort message: 'Pointer tag for 0x7920008aa0 was truncated, see 'https://source.android.com/devices/tech/debug/tagged-pointers'.'
```

下記のサイトを参照して、以下のようにしたらクラッシュしないようになりました。

```csharp:[プロジェクト].target.cs
        if (Configuration == UnrealTargetConfiguration.Shipping && Target.Platform == UnrealTargetPlatform.Android)
        {
            GlobalDefinitions.Add("FORCE_ANSI_ALLOCATOR=1");
        }
```

https://note.com/blastedge_note/n/n7324fdd49e1a

#### EpicOnlineServiceのログイン認証ができない

以下のようにエラーが出えてEpicOnlineServiceでDeviceIDによる認証が行えずホストを立てたり接続することができませんでした。

```text
LogRedpointEOS: Error: [LogEOSConnect] Failed to save DeviceId access credentials on the local device. Result: 2147483647.
```

どうもQuestのクラウドバックアップ機能がオンになっていて、VRアプリをインストールしおした際にクレデンシャルのファイルが復元されてしまいログイン認証ができなくなってしまう感じです。

Quest3の設定から[一般] > [クラウドバックアップ]でクラウドバックアップをオフにできます。
ただし、他のアプリのバックアップもされないようになります。

MetaQuestダッシュボードのアプリの[開発] > [クラウドストレージ]でアプリ単位のクラウドバックアップ設定をオフにできます。
対象のファイルパスを除外する設定もあるみたいなのでうまく設定すれば問題を発生させないようにできそうです。

https://communityforums.atmeta.com/t5/Unreal-Development/EOS-and-AppLab-login-issues-with-DeviceID/m-p/1211451

https://www.meta.com/ja-jp/help/quest/399621398228171/?srsltid=AfmBOoqZAQii0a-r6ncu8SKhCsA4tkVgf45O7fDTgjpBpzRtvLd3aQlJ

# 残課題

ひとまず動かして残った課題は以下のようなものになります。

* Pluginの機能を殺しているので利用できない機能があります
* マテリアルで正しく動作していないものがあります。パッケージング時にマテリアルに関するエラーが出ていたりします
* UnrealEngineが用意しているVoIPの機能を使ってボイスチャットをしているのですが、接続時やアイテムをスポーンしたときに接続相手に対して短時間の耳障りなノイズが発生しています
* Cesiumの地形表示で表示領域を広くしているとアプリが固まったり落ちたりしています