プロジェクト内の計画データの保存と読み取り

今回の主な話題：プロジェクト内の計画データの保存と読み取り、ASTCテクスチャの適用性、AndroidでのLightmapマクロの失効、モデルのUV情報を変更する方法、AssetBundleアセットのロードとアンロード。

---
###配置ファイル
Q1: プロジェクトの計画データはバイナリファイルを生成することであるため、最近、String値を取得するときに大量のMonoメモリ（byte[] to string）が消費されることが発見されました。すべてのExcelデータをScriptableObjectオブジェクトに変換しAssetBundleに入力した後、AssetBundle.LoadAsset（）が要求したMonoメモリはbyte[] to stringより大きいことがわかりました。皆さんのプロジェクト内の計画データはどのように保存および読み取っていますか。また、大量のテキストコンテンツのMonoメモリ消費を最小限に抑えるにはどうすればよいですか。

①
>配置テーブルのバイナリ、ScriptableObject、CSVはすべて一般的な方法で、Luaの重いゲームで配置テーブルをLuaファイルに転換してロードする可能性があります。配置テーブルをロードする時、Monoメモリの消費量が多いことには、様々な可能性があります。一つは、配置テーブル自身のデータ量が大きすぎです。もう一つは、同時にロードされた配置テーブルの量が多すぎで、要求されるMonoメモリを高めて、低下できません。

>配置テーブル自身が大きすぎる場合は、冗長データを減らすことができるかどうかを確認してください。あるいは、テーブルを分解して段階的にロードすることです。または、データを解析する時に割り当てられたメモリに注意して、ListやStringを再利用して頻繁なメモリ割り当てを減らします。

②
>私たちのゲームはLuaファイルを使用してデータを保存します。確かにデータだけでメモリが60MB占用する場合があります。いくつかのdbをデータとして使用する方が適切だと思います。使用する時には、対応するデータに移動してキャッシュすれば大丈夫です。また、一人のプレイヤーはプレイの過程ですべてのデータを使用するべきではありません。Luaまたは他のテキストファイルを直接使用する場合、それらの1つが使用されている限り、テーブル全体が追加されることを意味します。

③
>1）配置テーブルをAssetBundleパッケージにしないでください。これにより、ローディングスピードが低下するだけではなく、変換プロセスがありますから、大量のGCも生成します。Monoメモリも増加します。
2）シリアル化ファイルを少なく使用します。
3）重要なのは、配置テーブルを一気にロードするではなく、適当な時間にロードする必要があることです。Streamingが一部のデータをロードし、テーブル全体の読み取りに必要な時間とメモリ消費を削減します。

---
###アセットか管理
Q2: 最近、すべてのiOSデバイスにASTC圧縮方式を採用するつもりですが、ハイエンドのiOSユーザーに悪影響を与えることが懸念されています。しかし、ハイエンドデバイスとローエンドデバイスに2セットのリソースを使用することはできません。会社も小さいし、ハイエンドデバイスに対して何かが役立つもわかりません。フレームレートと発熱を改善できますか？

①
>テクスチャに対して、1つ目はファイルからメモリへの読み取り、2つ目はメモリからビデオメモリへのアップロードです（ロジック上のビデオメモリ、時にはグラフィックカード上の独立した一部であり、あるいはメモリから分割された一部であります。モバイルプラットフォームでは常にメモリから分割する一部であるようです。）3番目は、ShaderでTextureをサンプリングする場合、ビデオメモリからTextureを読み取る必要があります。

>RGBAとASTCを比較すると、後者のサイズがはるかに小さいため、「ファイルの読み取り/ビデオメモリへのアップロード」は理論的に高速であり、実際のユーザーエクスペリエンスへの影響は特定の状況によって異なります。

>テクスチャサンプリングを行う場合、通常、ターゲットテクセルの近くにある複数のテクセルを一度にキャッシュに読み取ります。後で隣のテクセルにアクセスする場合は、キャッシュから直接フェッチでき、ビデオメモリに再度アクセスする必要はありません。ビデオメモリへのアクセスは遅い操作であり、バッテリー消費も非常に大きいです（キャッシュへのアクセスと比較する）。データ送信は帯域幅を消費し、ビデオメモリへのアクセスの帯域幅消費はキャッシュへのアクセスよりもはるかに大きくなります。

>ASTCに対して、圧縮時にBlockで行い、読み取り中にもBlockで行い、そして専用GPUハードウェアを使用してBlockを解凍したから、Shaderに渡せます。

>RGBA32とASTCを比較すると、16バイトのデータが一度に読み取られると仮定すると、前者は16/4 = 4テクセル（各テクセル32ビット）、後者は少なくとも16テクセル（各テクセル8ビット）であるため、後でアクセスするとASTCのCacheヒットははるかに高くなります。つまり、全体として、ビデオメモリにアクセスする回数が大幅に減少します。

>ASTCは解凍する必要がありますが、設計された圧縮形式の解凍速度（ALU計算速度）は通常、データ転送速度（CPU計算速度とメモリアクセス速度の差など）よりもはるかに速いため、解凍コストを代償して帯域幅を交換することは良策だと思います。全体として、ASTCはRGBAよりも高速で、バッテリーを節約します。同様に、それがユーザーエクスペリエンスにどの程度影響を与えるかは、実際の状況によって異なります。私たちにはできる限り圧縮し、できるだけ高く圧縮します。 フレームレートに影響を与えるかどうかは、ボトルネックがテクスチャ帯域幅にあるかどうかによって異なります。

②
>1）プロジェクトチームに資金またはメンバーが多い場合、2セットのAssetBundleパッケージを作成します。ipaにはASTCバージョンが付属しており、iPhone5sおよび以前のデバイスはRGBA32 + PVRTCバージョンのAssetBundleパッケージをダウンロードします。
2）プロジェクトチームは余裕がなく、ボスやオペレーターは99％のiOSデバイスでの互換性を要求している場合、RGBA32 + PVRTCを続行する必要があります。
3）プロジェクトチームは余裕がなく、ボスやオペレーターはiPhone5sと以前のデバイスを放棄することを許す場合、ASTCに決めます。

③
>解像度は1024×1024である場合、下図はメモリサイズを示しています。RGBA32が最高の効果と最大のメモリ占用を備えています。ASTCは小さく、効果もETCより圧倒的です。バッテリー消費とフレームレートに向上できますが、効果は非常に小さいです。ほとんどの発熱はまだCPUとGPUの計算上のプレッシャーです。
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/9c19cda8-f17f-a946-fa67-60c218acfc4f.png)

④
>私も最近この問題について考えています。ASTCを使用すると、ハイエンドのiOSユーザーを傷つけます。 ASTCフォーマットは現在iOSで推奨されているフォーマットです。品質とパッケージ本体のメモリの読み込み効率の点でバランスの取れたフォーマットであると言えます。 欠点は、5s以前のデバイスをサポートしていないことです。さらに、非可逆圧縮でありますから、特に半透明UIで、RGBA32との品質的の差ははっきり見えます。

>現在の暫定的な計画は、2セットのアセットを準備することです。ipaにはASTCが付属しており、半透明UIなどの差がはっきり見えるところに対して、ハイエンドのiOSユーザーだけが見ることができるRGBA32リソースをもう１セット準備します。しかし、このアセットはユーザーにダウンロードを強制する必要はなく、単なるダウンロードオプションです。ダウンロードのタイミングは必要に応じて設定でき、ダウンロード後に通知することもできますが、キャンセルすることもできます。内部に高解像度のリソースをダウンロードするオプションがあることもでき、当然、ユーザーがアイドル状態のときにバックグラウンドでwifiを介してダウンロードすることもできます。もちろん、デザインの目的は、ユーザーエクスペリエンスに影響を与えることなくプレーヤーに選択肢を提供することです。

>このような処理方法の利点は、適応する必要がある場合、5s以前のデバイスもサポートできることです。

---
###Lightmap
Q3: プロジェクトの一部のシーンはライトマップを使用し、一部のシーンはライトマップを使用しません。ShaderでLIGHTMAP_ONスイッチを使用してライトマップを読み取ると、PCでロードされたシーンAssetBundlleアセットにLightmapがありますが、Android端末にリリースされたLightmapが読み取られないことがわかります。このマクロスイッチは無効になったそうです。何か解決方法ありませんか？

```ruby:
#ifdef LIGHTMAP_ON
half4 lm = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lmuv.xy);
lm.rgb = DecodeLightmap(lm);
#endif
```

>Shader SrippingにあるLightmap Modesを変更して、自動にしないでください。自動の場合、現在開いているシーンに応じてパッケージ化されたAssetBundleが設定されます。空のシーンの場合、Lightmapは全部失われます。

---
###FBX
Q4: モデルのUV情報を変更しました、AssetDatabase.SaveAssets()を使用して保存しましたが、Unityを再起動した後に復元されました。保存するにはどうすればよいですか？FBXを開いて情報を確認するのは、Maxにインポートして戻す必要がありますか？ EditorUtility.SetDirtyはもうコールされています。
![2-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/5e5cf5e2-9a25-c18c-c17b-1140549a105e.jpeg)
![3-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/8e3782f9-d85c-5f4b-b84a-cf78e112ae52.jpeg)

①
>最善の方法はインポート時にOnPostProcessModelでUV変更を行うことらしいです。同じ名前のMesh Assetを作成して、抽出したMeshを使用することもできますが、抽出されたMeshの問題は、FBXとの接続が切断されることです。外部ツールによってFBXを変更する場合、インポートして再抽出する必要があります。なた、FBXのインポートプロパティを設定する場合は、設定後に再抽出する必要があり、不便です。

②
>簡単な方法は、FBXからMeshを一セットコッピーしてから変更することです。FBXは読み取り専用であり、上書きできないため、ずっと抽出されたMeshを使用すれば大丈夫です。

---
###アセット管理
Q5: 現在、プロジェクトのすべてのアセットは、AssetBundleを使用しています。次の質問があります。

1）現在、すべてのリソースは依存関係情報に従って、自分自身と他のAssetBundleをロードし、それらはすべてキャッシュされており、破棄されていません。これにより、メモリがどんどん大きくなりますよね？

2）すべてのAssetBundleをUnloadする必要がありますが、主観的に判断してアンロードすることは絶対に適切ではありませんでしょう。では、何かいい管理策ありませんか？リソースとAssetBundleパッケージに引用カウントによって管理されている場合、それはすべてのロード場所のアンロードを管理する必要があることを意味しますか？個人的には、プロジェクトが巨大な場合、ロードする場所が多すぎるため、適切ではないと思います。

3）Unload（false）を使用して管理する場合、メモリ内のリソースが大量に重複することを意味するでしょうか。ロードされているAssetBundleが多すぎるかもしれません、シーンを切り替えるときにResources.UnloadUnusedAssets();の効率が大幅に低下しました。

4）画像にはPackingtagが設定されていますが、Shaderによって使用されているため、最終的にアトラスとしてマージされない可能性がありますか？

①
>全部キャッシュするとメモリは確実にどんどん大きくなるので、適切な解放策が必要です。

>unload(false)がAssetBundleと内部のAssetの関係を切断しました。再びロードすると、前のアセットが解放されない場合、アセットが重複します。自分で管理する必要があります。ロードするところはたくさんありますが、統一して管理できます。自分で引用カウント管理も含め、インターフェイスのロードおよびアンロードを統一すれば、独自のアンロード戦略を作成できます。たとえば、シーンを切り替えるときは、より徹底的なクリーンアップを実行し、コアの戦闘でアセットをクリーンアップしないようにし、特定の状況に応じてキャッシュを並べ替え、解放策を指定します。

>最後の質問にはテストされていません。それが自分のShaderであれば、アトラスにできる場合、正しく表示するために自分でUVを処理する必要があります。

②
>1）メモリは増え続けます。ヘッダーファイル+アセット本体。

>2）引用カウントが主な方法です。大規模なプロジェクトでは引用カウントが必要です。アセットをロードするところやタイミングが多すぎるために、インターフェイスを統一して統合管理の目的を達成します。

>3）複数のアセットがある原因は、AssetBundle内のアセットをロードしてインスタンス化した後、アセットをアンロード（false）してから、再びAssetBundleをリロードしてアセットをインスタンス化しました。この種の問題は、一般に、ShaderやMaterialなどのロードを主動的要求しないアセットによく見えます。

>4）アンロードするタイミング：
ポーリングアンロード：非ロード期間中、ポーリングを行い、アンロードリストを作成し、フレームでアンロードし、ジャムを防止します。
破棄アンロード：特定のUIまたはエンティティが破棄され、引用カウントが0である場合、X秒後にトラバースアンロードを主動的に要求します。

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

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