ARLyra!! UnealのサンプルゲームLyraをARサポートさせてみよう!! 

<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/ARLyra?src=hash&amp;ref_src=twsrc%5Etfw">#ARLyra</a> 遂に悲願の、影ポリが見えない綺麗なAR shadowが出来た!!😆 禁断のエンジンシェーダーいじりしちゃったけど、実現出来る事が分かったのは大きい。<br>あとAIもちょっと修正して、遊べるようになった…、か？😅<a href="https://twitter.com/hashtag/UnrealEngine?src=hash&amp;ref_src=twsrc%5Etfw">#UnrealEngine</a> <a href="https://twitter.com/hashtag/UE5?src=hash&amp;ref_src=twsrc%5Etfw">#UE5</a> <a href="https://twitter.com/hashtag/AR?src=hash&amp;ref_src=twsrc%5Etfw">#AR</a> <a href="https://twitter.com/hashtag/ARCore?src=hash&amp;ref_src=twsrc%5Etfw">#ARCore</a> <a href="https://twitter.com/hashtag/Pixel7?src=hash&amp;ref_src=twsrc%5Etfw">#Pixel7</a> <a href="https://t.co/L6EYwhOZ7V">pic.twitter.com/L6EYwhOZ7V</a></p>&mdash; LazyKozy (@LazyKozy) <a href="https://twitter.com/LazyKozy/status/1749964020059361659?ref_src=twsrc%5Etfw">January 24, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# はじめに
[ARグレイマンを歩かせてみよう!!](https://qiita.com/LazyKozy/items/37a8157007058289bd3c)
さて、この記事を投稿したのがなんと3年前:sweat_smile:
諸般の事情でまた自由時間が出来てしまったので😥
Lyraの調査ついでに、世界初（たぶん😅）Lyra ARサポートをさせてみました。Lyraそのまま動いてるのでちゃんと（？）FPSとして遊べます。懸案事項のAR Shadowも表示されてます😆
[Lyra サンプル ゲーム](https://docs.unrealengine.com/5.3/ja/lyra-sample-game-in-unreal-engine/)に関してはUnrealの公式ドキュメントを見て下さい。

# 開発環境
UE5.3.2
デバイス Pixel7 (ARCore)
# とりあえず縦画面
やっぱARは縦画面だよね! という事でとりあえずAndroidビルドを縦画面化してみましょう。
```\Config\DefaultEngine.ini
[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
Orientation=Portrait
[/Script/Engine.UserInterfaceSettings]
ApplicationScale=2.000000
```
```\Plugins\GameFeatures\ShooterCore\Content\UserInterface\W_ShooterHUDLayout.uasset```
レイアウトはこのウィジェットで定義されているので、いい感じにレイアウトを変更します。
これでAndroidで縦画面になりました!! 

# ARモードをスタート
```\Config\DefaultEngine.ini
[/Script/AndroidRuntimeSettings.AndroidRuntimeSettings]
bSupportsVulkan=False
```
私の知る限り、未だにAndroid-VulkanではARモードは起動しません:disappointed_relieved:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/685afb72-6aee-5cd9-88c3-d7f20ed81f8e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/2ae90259-94a8-08b8-644a-92c3e9748729.png)
どのタイミングでAR Sessionをスタートさせるのかどうかは、ゲームによって変わると思いますが、とりあえず`B_LyraPlayerController`を新規作成して`BeginPlay`からスタートさせました。
Godモード、Postprocessをオフ、AR World Originを変えてカメラの位置を目線にセットしています。
これを`B_LyraGameMode`にセットします。

# AR Shadowを設定
```\Config\DefaultEngine.ini
[/Script/Engine.RendererSettings]
r.AllowStaticLighting=True
r.Shadow.Virtual.Enable=0
r.Mobile.EnableStaticAndCSMShadowReceivers=True
```
とりあえず私のコンフィグの変更はこんな感じです、以下のサイトなどが参考になります。
[TIP: How to enable Dynamic Shadows & Correct Reflection Maps on mobile](https://forums.unrealengine.com/t/tip-how-to-enable-dynamic-shadows-correct-reflection-maps-on-mobile/30527)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/d9afe263-16e2-2d4d-1c5b-a41eed127a19.png)

`ShadowMesh`を作って、丸影マテリアル/テクスチャをセット。AIキャラやネットワークキャラはこの丸影を使います。
これを`\Plugins\GameFeatures\ShooterCore\Content\Game\B_Hero_ShooterMannequin.uasset`のコンポーネントに追加します。
ローカルキャラクターにはAR Shadowを適用します。マテリアルは以前の記事に沿って作ります。
[ARグレイマンを歩かせてみよう!!](https://qiita.com/LazyKozy/items/37a8157007058289bd3c)
キャラクターBPに少し変更を加えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/dfb39ecf-25b1-d164-bea7-85f9c18480a1.png)
`Event Possessed`に追加。`Begin Play`では`IsLocalPlayerController`が取得出来ないので、ここに足します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/dc80104a-ff52-0fed-7fdb-795eba4eb941.png)
`Event Tick`に追加。AR Shadowの更新と、ジャンプした時に`ShadowMesh`が常に地面に張り付く処理を足します。

# AR Shadow用にシェーダーを変更
影ポリがライティングの影響を受けて、微妙に影ポリが見えてしまう問題が、どーーうしても解決出来なかったので、EngineのShaderをちょっと変更してしまいました:sweat_smile:
あまり褒められたやり方ではないので参考程度に…。
UE5.3 `\Engine\Shaders\Private\MobileBasePassPixelShader.usf` Line:890
UE5.4 `\Engine\Shaders\Private\MobileBasePassPixelShader.usf` Line:989
辺りに、これを追加。
影が落ちたピクセルは必ず暗くなるので、ライティング適用前のピクセルを比べて明るくなっている場合は除外させます。この変更を使ってないClearCoatにのみに適用させます。
```
#if MATERIAL_SHADINGMODEL_CLEAR_COAT
	DirectLighting.TotalLight.r = min(DirectLighting.TotalLight.r, BaseColor.r);
	DirectLighting.TotalLight.g = min(DirectLighting.TotalLight.g, BaseColor.g);
	DirectLighting.TotalLight.b = min(DirectLighting.TotalLight.b, BaseColor.b);

	DirectLighting.TotalLightDiffuse = 0;
	DirectLighting.TotalLightSpecular = 0;
#endif
```
それから、ShadowマテリアルをClearCoatに設定します。

# 入力スキームを変更
二本スティックモードでラジコンみたいに動す事は出来るのですが、これだとARっぽくないので常にカメラの前にキャラクターが移動するようにしてみました。
もの凄く大雑把に言うとこんな感じです。
```
void ALyraPlayerController::PlayerTick(float DeltaTime)
{
...
    // ターゲットに移動
    if (StartMoveDist*StartMoveDist < DistSquared2D)
    {
	    UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, MoveTargetLoc);

    // キャラクターの向きをカメラと同じに
    {
	    bAutoManageActiveCameraTarget = true;
	    CurrentPawn->FaceRotation(PlayerCameraManager->GetCameraRotation());
```
# Rifle/ShotGunの発砲をタッチ操作で行えるようにする
Winビルドだと発砲出来るのに、タッチ操作にするとRifle/ShotGunの発砲が出来ない問題で悩んでいたのですが、これの追加で解決しました。
`\Content\Input\InputData_Hero.uasset`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/5b42029b-2150-ae91-e93e-8b2e8798c86f.png)
`IA_Weapon_Fire_Auto`がタッチ操作では発行されないのが問題でした。正しい解決法とは言えるかどうかは微妙ですが、まあとりあえずオッケーでしょ:sweat_smile:

# マップを設定
```\Plugins\GameFeatures\ShooterCore\Content\Maps\L_ShooterGym.umap```
このマップから余分なオブジェを削除して作りました。グラウンドは`HideInGame`にします。
[TIP: How to enable Dynamic Shadows & Correct Reflection Maps on mobile](https://forums.unrealengine.com/t/tip-how-to-enable-dynamic-shadows-correct-reflection-maps-on-mobile/30527)
ライトの設定に関してはこの投稿が参考になります。
重要なのは、以下の通りです。
1. `LightmassImportanceVolume`を置く
1. `SkyLight`は`Static`。`Cast Shadows = false`
1. `DirectionalLight`は`Movable`。`Cast Shadows = true` `Cast Dynamic Shadows = true`
1. レベルのビルドをする。

# とりあえず動きました!!
最初のムービーの様にとりあえず動いてます。
ちなみにGodモードを外すとAIに秒殺されます:sweat_smile:
まあやっぱりフルFPSをARでプレイさせるのは無理があるので、ポケモンGoみたいなスタイルか、目の前の相手とやり合うテニスみたいなゲーム以外は難しいですねぇ:thinking:

もうちょっとちゃんとプレイ出来るようにしてみたいのですが、シングルプレイだとAIをがっつりと変えなきゃいけませんね。
マルチプレイは簡単なはずですが携帯を一台しか持ってないからテスト出来ない…。Windows<->Androidでのクロスプレイなら、自分一人でもテスト出来るかも？:joy:

あと`SceneDepthForOcclusion`が上手く動きませんでした:thinking: これは要調査事項です。