コールドスタート時間を最適化する方法

今回の主な話題：UniRx使用経験の共有、特殊効果はテクスチャマージの最適化に値ませんか？、AssetBundle LZMAとLZ4圧縮、iOSレンダリング関するクラッシュ問題、Shadows.RenderShadowMapの時間のかかる最適化。

---
###ローディング
Q1: プロジェクトを最初に開始するときのコールドスタート時間が長すぎる可能性がありますが、どうすれば最適化できますか？
>まず、コールドスタートプロセス中にゲームが何をしているのかを判断するのに時間がかかり、特定的に最適化を行う必要があります。コールドスタートの限界も定義する必要があります。ゲームをクリックしてからUnity画面が表示されるまでの時間は通常コールドスタート時間として定義されますが、私たちのプロジェクトでスタートの最適化をする後、リソースの初期化にまだ長い時間が費やされていることが発見されました。

>起動時間を最適化する過程に、特に何かのツールを使っていませんでした。主にmlogcat.exeに基づいてデバイスの情報を出力するlogをチェックし、自分が追加したlogを組み合わせて問題を検出します。

>単純にコールドスタート時間が長すぎると、Resourcesディレクトリ内のリソースに関係があります。リソースが多くと遅くなります。私たちは主にAssetBundleメソッドを使っているので、この部分にも注意しました、あるプラグインが導入した不要なリソースをクリーンアップしました（パッケージ化されたapkのコンテンツを直接見て確認します）。

>ゲームの起動時間がかかりすぎる問題に遭った時に解決したいくつの問題についてお話しします。私たちが定義した起動時間は、appアイコンをクリックしてからゲームPatchインターフェイスに入るまでの時間であります（つまり、ゲームロジックの引き継き）。

>1）Shaderのコンパイル時間が長すぎです。ゲームがインストールされた後に初めて起動する時の時間だけが長すぎる場合、一つの大きな可能性はShaderコンパイルであります。この後にCacheがあるため、ゲームの起動時間ははるかに早くなります。この場合には、Always IncludeのShader内容と変体を確認して、代わりにshadervariantcollectionなどの方法を使用することをお勧めします。

>2）ToluaバインディングとLuaリソースのローディング。これらのは毎回ゲームを起動する時にあるものです。ToLuaインターフェイスのバインドには一定の時間がかかります。私たちは、初期にLuaが使わず状況を確保してマルチスレッド方式でバインドとローディングを行い、メインスレッドがジャムしないことを保証します。

>3）Webリクエストのタイムアウト期間の設定に注意してください。ゲームが起動する時にいくつかのhookを行ったところ、Webリクエストが発生しました。その後、多くのマシンで黒い画面が30秒または60秒以上待つ状況に遭いましたから、マシンのデフォルトのタイムアウト時間を使用しました。デバイスによって異なります、たとえば、Redmi2Aのタイムアウト制限は1分近くです。これは厄介なものです、ただあの必要ではないWebサービスが正しく開始されなかったという理由だけで、調査に長い時間がかかりました。

>ネイティブレイヤーでインターフェイスを増やし、黒い画面の待機時間を減らし、プレーヤーのエクスペリエンスを向上させます。実際に、これは問題を解決する方法ではなく、単なる緩和方法です。最適化が実施されれば、それは必要なくなります。

>言及されている内容のほとんどはコールドスタートではなく起動時間ですが、問題主が参照できます。問題主にUnityプロセスの出力logをよく確認することをお勧めし、何か予期以上のものが発生する可能性があります。一般的に、Resourcesの方式を使用しないなら、Bugがなければコールドスタート時間はそれほど長くないはずです。この方法を使うことがないため、あまり明確ではありません。

---
###動画
Q2: 現在 はSective（false）を介してPoolに配置し、使う時にPoolからSetActive（true）を取得やコールしてアニメーションを再び再生します。欠点は、Animator初期化などの時間のかかる操作を引き起こしやすいです。SetActive操作をしなくで代わりにScaleを0に設置すれば、特殊効果の続きのレンダリングを回避できますが、AnimationとParticleがずっと実行している、同じにCPUリソースをいくつ浪費しませんか？どうしたらいいですか？
>AnimatorとAnimationが配置されているGameObjectがActivateする時に確かにより明らかななコストがあります。

>一般的に、アニメーションコンポーネントに適用されるパーティクルエフェクトが少ない場合、この部分の影響はそれほど大きくありません。この2つの部分の具体的な消費率を確認することをお勧めします。高くない場合は、現在の計画を維持できます。 この部分に時間がかかる場合は、アニメーションコンポーネントとパーティクルシステムコンポーネントTransformノードをできる限りに分離します。例えば、親ノードでAnimator/Animationコンポーネントをぶら下げ、子ノードにParticleSystemをぶら下げ、Activate/Deactivateの時に子ノードでのみ操作します。親ノードでEnable/Disable Animator/Animationコンポーネントの方式を採用すればこの部分のコストを回避できます。

---
###Monoメモリ
Q3: Unity公式のMemory Profilerを使ってテストしたところ、集まったManagedObjectSizeとProfilerが顕示するマネージドヒープの使用済みのUsedHeapSizeは大きく異なります。UsedHeapSizeはManagedObject Sizeの約10倍であります、何から統合されていませんか？
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/d83c170b-20f9-33b3-0cb0-c97e9b74acff.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a9c3e50a-5c80-b5b8-dbf2-416795bf75f6.png)
>テストを行いました、同じような状況を再現できます。

>ProfilerのUsed Monoはやく500KBであります。
Unity 公式のMemory Profilerはやく50KBであります。
UWA GOTのDirect モードを使ってテストした結果もやく50KBであります。

>したがって、この約450KBの違いがMono自体によって生成されたものかどうかを推測することしかできません。それを確認するために、Monoのソースコードを確認する必要があります。また、この場合は確かに「約10倍」ですが、Monoメモリが比較的に大きい場合のテストをお勧めします。差の割合が非常に小さい可能性があります。

---
###適応
Q4: ゲームは他の携帯電話では正常ですが、デフォルトでMi MIX 2の右側に黒いバーがあります。携帯電話でフルスクリーンをオンに設定する必要があります。ただし、いくつか他のゲームの場合、手動で設定しなくても正常でありました。デフォルトで全画面設定をオンにするにはどうすればよいですか？
>AndroidManifest.xmlでmax_aspect値を声明します。

>全画面スマホのアスペクト比が以前よりも大きいため、適応しない場合、デフォルトでAndroidの最大アスペクト比は1.86であり、全画面スマホのアスペクト比よりも小さくなります。ですから、全画面スマホで一部のアプリを開くと、上下にスペースがあり、黒いバーで表示され、視覚体験に大きな影響を与えます。一方、全画面が提供する余分なスペースも使用されないため、これらのアプルが相関適応を行う必要があります。

>これに対して、Android公式が適応ソリューションを提供します。つまり、アプリが支持する最大画面アスペクト比を上げることです。実装も比較的簡単です。AndroidManifest.xmlで次の構成を行うことができます。
< meta-data android:name=“android.max_aspect” android:value=“ratio_float”/ >

>その中で、ratio_floatは浮動小数点数であり、18.5：9 = 2.055555555 ……であるため、公式の推奨値は2.1または以上です。将来、アスペクト比がより大きいスマホが出現する場合、この値を大きく設定する必要もあります。

>したがって、開発者にアプリAndroidManifestのApplicationタグの下に次のコードを追加することをお勧めします。
<meta-data android：name =“ android.max_aspect” android：value =“ 2.1” />

---
###Lua
Q5: Lua部分、functionを使って一つのdelegateを作成し、C#に入力してイベントリスナーとします。ただし、監視が終了したら、delegateを空にしただけ、LuaFunctionを解放しない場合、メモリリークが発生します。 これに良い解決策はありますか？
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/5e73b1ae-4f3e-f821-919d-c77888c7fb32.png)
先に+=を使い、後に-=を使うことは問題がないはずで、LuaFunctionを解放できます。ただし、ビジネスロジックの原因で、Luaレイヤーで加算と減算操作を同時にするのは難しいです。C#の部分で全ての追加されたものを安全に解放するのが一番いいです。
>FairyGUIは委任されたインターフェイス（class EventListenerのみ）を公開しないため、一部の底層コードを変更する必要があります（変更は大きくないです）。

>下記に一つのソースコードを変更する必要のない実現方法を紹介します。

>1）Lua DelegateMgrに委託がどれのC#に引用されることを記録して、そして不要な委託を定期的にクリーンアップします。
![4-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/87a875f6-5be7-cb4f-efa5-ba5a98aac5b3.jpeg)
2）いくつかのFairyGUIをカバーする方法でFairyが作成する委託をDelegateMgrに納格して管理します。
![5-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a90a8d8f-5ced-9455-38b7-7daffc9a9bc1.jpeg)

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

UWA公式サイト：https://jp.uwa4d.com
UWA公式ブログ：https://blog.jp.uwa4d.com