Crunch圧縮画像のAssetBundleパッケージ

今回の主な話題：Crunch圧縮画像のAssetBundleパッケージ、MeshBakerがMeshと合併すると面の数が２倍になる、画像の多言語実装、WWWがAssetBundleメモリをロードする。

---
###アセット管理
Q1: Crunch圧縮画像のAssetBundleパッケージについて質問があります。Unityの公式ウェブサイトでAssetBundleの使用紹介に下記のような言葉があります。Crunch圧縮した画像をAssetBundleパッケージにして再び圧縮すると、サイズは基本的に変更しませんが、梱包が遅くなり、使用時に解凍が必要となるため、圧縮はお勧めしません。
4.6.1. Crunch Compression

Bundles which consist primarily of DXT-compressed textures which use the Crunch compression algorithm should be built uncompressed.

しかし、AssetBundleをパッケージする時に、AssetBundleごとに圧縮するかどうかのオプションはありません。全部を圧縮します、または全部を圧縮しません。ですから、「Crunch を使用して圧縮された画像をパッケージする時に圧縮しないことをお勧めします」という考えに従って、私は2つの方法を考えました。

1）すべてのAssetBundleパッケージを圧縮しないようにします。しかし、こられてTexture以外のアセットのAssetBundleパッケージがさらに大きくなるかどうかはわかりません。経験のある人がいればぜひ教えてください。
2）最初にLZ4で全部のパッケージをパッケージ化する、次にAssetBundleManifestファイルをバックアップし、非圧縮方式でCrunch圧縮した画像パッケージをパッケージ化し、この前にバックアップしたAssetBundleManifestで上書きします。このやり方には少し奇妙だと感じで、画像のAssetBundleパッケージにも２回パッケージ化され、パッキング時間を延長します。

何かよりいい方法ありませんか？それとも圧縮しなくならいいですか？


①
>確かにたまに選択しにくいものはいくつあります。私なら直接にLZ4を使用する方法を選択するきです。何といっても、LZ4は自体の圧縮率を犠牲として「does not require the entire bundle to be decompressed before use」という利点と引き換えました。Crunch圧縮したテクスチャの圧縮空間はあまりありませんが、LZ4での解凍時間はそれほど長くありません（具体的には、問題主は自分でテストする必要があります）。

>AssetBundleを圧縮しないことは納得できないと感じます。この前に簡単でパッケージ全体を見たことがありますから、しないとパッケージサイズは大きくなることはわかります。2回パッケージ化の過程で、私個人的には価値のないと感じており、いくつかの欠点もあるかもしれませんで、発見するために時間がかかります。パッケージ化に対して、プロセスは単純な方が間違いなく優れています。

>使用したプロジェクトはまだUnity5.6バージョンであるため、Crunch関連のテストは行っていません。問題主に、みなさんのアドバイスを集めること以外に、自分でテストして検証することもお勧めします。例えば、完全に圧縮しないことがパッケージの影響や、LZ4圧縮を使うこととテクスチャを圧縮しないことがローディング時間への影響など。本当のテストデータは問題主が決める時に役に立つと信じています。

②
>二つ策略があります。

>1）LZ4圧縮、パッケージ化時間は許容範囲内です。
2）AssetBundleを圧縮しません。パッケージされたAssetBundleをzip圧縮し、ゲームを初めて初期化する時に、読み取りと書き込みが可能なディレクトリに解凍します。これでUnityが圧縮していないAssetBundleファイルを直接読めるし、将来の統合ホットアップデート管理にも便利です。

③
>前の人たちが言ったのは主流の方法です。LZ4はパッケージとローディングスピードに良いバランスがあります。自分が圧縮し、そして読み取りと書き込みが可能なディレクトリに解凍し、CreateFromFile方式でロードすることは、メモリと効率両方でも利点があります。代償は、前に長い解凍過程があることです。この時にインターフェイスの応答が必要です。ゲームコンテンツ画面のない時間が長すぎるとAppleの審査に通れない可能性が高いです。

---
###レンダリング
Q2: MeshBakerを使用して、全シーンにあるShaderの同じオブジェクトをできる限りに合併します（合併Meshの合計面の数が64K以内）。Meshを合併完了後に、シーンのDrawCallが低下しましたと見つかりましたが、Statsパネルの面の数は2倍になりました。カメラの錐台カリング機能は合併完了のMeshBakerに無効になっているようです。この問題を解決する方法は？


①
>CPUがGPUに送ってレンダリングする前の最小単位は一つのMesh（または一つのSubmesh？）であるため、手動的にMeshを合併すれば、面の数が増える可能性が大きいです。これは面の数とDrawCallの間の選択です。プロジェクトのボトルネックに応じて適切な平衡点を選択することをお勧めします。

>UnityのStaticBatchを使用しているため、Batchesを減らすことができ、面の数は増えません。原理はSubmeshに合併して並べ替え、カメラやSubmeshによって裁断します。もちろん、代償もあります。パッケージとメモリのサイズは大きくなる場合があります。そして並べるだけので、ダイナミックシャドウなどのマクロの設定により、Batchが中断される可能性もあります。

>簡単なテスト検証をやりました。２つのCube、合併したら、
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/ba9a327b-9bb5-a6da-8f92-7ee2d09a8110.png)
カメラを回転して、視角に１つしかない場合に、
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/6007785a-2523-4587-b0ae-3de285271119.png)
Demoは比較的単純で、前の観点を確認するためだけに、問題主はそれを確認するために自分で複雑なデモを作成できます。

>世界には無料のランチはありません。 ほとんどの最適化には代償が伴い、他のものと交換されますが、価値は異なります。

②
>前の人が言った通りに、これは客観的な事実です。だから私は下記の計画を選択します。

>静的バッチ処理を中止し、手動合併を実行します。合併の方法は視野距離に応じてブロックに分割され、シーンがロードされるときに小さなブロックのMeshがマージされるため、DrawCallの数は増加しますが、あまり多くではありません。三角形が大幅に削減されます。

③
>補足します。手動合併とこの問題が発生しますが、手動合併するときに粒度を自分でコントロールできます。もちろん、それでも柔軟ではありません。 Static Batchにはメモリ問題とパッケージサイズが大きくなる問題があります。

>Unityは、実行時に合併できるAPI「StaticBatchingUtility.Combine」を提供しています。2つのオーバーロード「public static void Combine(GameObject staticBatchRoot);」「public static void Combine(GameObject[] gos, GameObject staticBatchRoot);」がこのAPIで合併粒度を制御できます。静的バッチ処理のように、これはパッケージを増加しません、代償はメモリとCPUのコストです。前の人が言った通りに、世界には無料のランチはありません。最適化する時に通常、別の価格がかかります。

---
###メモリ
Q3: バージョン2018.2.13f1でWWWを使用したAssetBundleのメモリをロードすることに関する問題があります。WWWがメモリをロードした記録は下記のように、

最初に、
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/06fa33e5-3e93-9112-80ee-fe762e49fcee.png)
WWWダウンロード中、
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a32ce833-9c38-5fbf-6484-7634ab71f26e.png)
www.assetbundleをコールした後、
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/c5cb1f2c-561e-95d1-087f-5c00f6de0b27.png)
www.dispose後、
![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/2a8ab434-463b-9241-0f69-0e8b286cbc9f.png)
ab.unload後、
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/1f48e270-ab5a-b64a-d341-2f0b4bd6c5ca.png)
www.assetbundleをコールした後、Monoが36MB増加し、Unityが26.1MB増加しました。この「26.1MB」はどう理解しますか？ダウンロードしたAssetBundleファイルはUnCompressedであり、サイズは36MBです。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/858b3d17-0ad8-0033-e6b6-77d6ef317efc.png)
>表にあるメモリ統計は2018年（おそらく2017.4）にはもうあまり正しくないはずです。

>問題主のNew WWWローディングを例として、この前に36MBのUnCompressed ABをロードする時、このメモリは36+36=72MBでしたが、現在には36+36+26=98MBであります。

>前の72MBは全部Unityに統合され、現在にはUnityの中の62MBとMonoの中の36MBに分けます。
この原因は、Unityエンジンが新しいバージョンで（具体的に2017のどのバージョンからのはわかりません）、New WWWがABをロードするとwww.bytes操作を実行し、そしてLoadFromMemoryを介してロードします。これも36MBのUncompressedがロードした後に、Monoは36MB増やす原因です。次にLZ4とLZMAのABローディングに、MonoもSizeと同じ大きさのサイズを増加します。

>同時に、余分な26MBは、LoadFromMemoryのローディングが引き起こします。ここで忘れたものがあります。36MBのUnCompressed AssetBundleも10MBのLZMA AssetBundleも、www.assetbundleをコールしたら、Unityメモリの増加は全部26MBですが、20MBのLZ4 ABの増加は20MBであります。ここで二つの推測があります。

>1）LZ4 AssetBundle bytesがLoadFromMemoryを介してロードされると、直接にLZ4のAssetBundleに変更して使用します。一方、UnCompressedとLZMAはLZ4のような他のパラメーター形式に圧縮されるため、メモリに違いがあります。
2）どの圧縮形式のbytesでも、すべてLZ4形式に変換します。ただし、UnCompressedおよびLZMA形式のデータはさらにデータ変換をする必要があるため、一つのBufferを開いて変換を行います。だから6〜7MBの余分が発生した原因はBufferであります。

>もちろん、上記の２つはソースコードを見ていない状況で推測します。個人的には1番目の方が好みですが、他の原因が存在する可能性もあります。続きに、他のAssetBundle APIのローディング方法についても多くのテストを実施する予定ですが、更新が必要なコンテンツが多いようです。他の友達が同じようなテストを行ったり、他の記事を読んだりした場合は修正や共有することも大歓迎です。

---
###UI
Q4: 現在は台湾版の多言語対応をやっています。つまり、簡体字中国語版から繁体字中国語版へ変更します。画像サイズの問題は考慮しません。現在の配置したい範囲は、企画テキスト、UIテキスト、UI単一画像、企画が配置した画像、UIアトラス、企画が配置したアトラス、UI Prefab、キャラクター、キャラクターのスキル及び特殊効果であります。

企画テキストとUIテキストに関して、テーブルをインポートやエクスポートすることで解決しました。現在、様々な画像が残って、単一画像について、言語を切り替えるときに、すべてのPrefab、Assetをトラバースし、画像の多言語バージョンを引用し、解決策と見なすことができます。今はアトラスの部分はまだあります。私には2つのアイデアがあります。

一つは、画像をパッケージするツールを変更します。パッケージする時にさまざまな言語の画像を選別して、現在の言語を選択してそれらをアトラスにパッケージ化することです。利点は、維持が簡単で、複数の異なる言語のアトラスを維持する必要がないことです。欠点は、画像のパッケージ化に時間がかかります。毎回言語を切り替えるのに長い時間がかかり、直感的ではなく、2つのアトラスの違いを比較することはできません。

もう一つ方法は、2セットのアトラスを作成し、拡張子で異なる言語を区別してから、単一画像の多言語ソリューションを模倣して引用を変更することです。利点は、画像をパッケージしまして、変更がない場合は、切り替え時に再パッケージ化する必要はありません。高速で、比較することも簡単です。欠点は、２セットのアトラスを維持する必要があり、少し面倒くさいです。

①
>他の言語のアトラスはありません。やり方は、アトラスにテキスト付きの画像を取り出し、Textureを使用して言語に応じてロードすることです。後の新しいプロジェクトでは、デザインから文字のある画像をアトラスに入れないと決定します。複数のアトラスの問題は、言語を動的に切り替えたい場合は、対応するアトラスも切り替える必要があり、アセットとメモリの冗長性を考慮する必要があることです。

②
>AssetBundleのVariantsを使用して多言語を作りました。プリセット、アトラス、およびテクスチャをすべてバリアントにすることができ、基本的に様々な要件に満たされます。利点は、ドラッグされた引用を変更する必要がなく、UnityのVariantsが解決することです。欠点は、エディターで直接プレビューすることはできないことです。先にAssetBundleをビルドする必要があります。

③
>前に私がやりました時には、テキスト、アトラス、Prefab、Audioなどを分けて考えます。考えすべきものは、各種言語の長さの違いが導く植字しにくいことです。アトラスにある、様々な言語が一緒に置いてAssetBundleを作る時に言語を選別します。多言語画像のサイズも違うかもしれないで、アトラスの計画に注意し、2つのアトラスに分割しないようにしてください。

---
###UI
Q5: UGUIフォントが太字になった後、Android実機では太字にならず、拡大するとテキストが重複します。インターネット上で解決策は見つかりませんでした。 それを解決する方法がわからない。
>msyhを使用する場合は、アセットにmsyhとmsyhbdの両方を配置する必要があります。それでUGUIでmsyhの太字フォントの使用ができます。そうしないと、Unity自体によってのみmsyhが太字になります。

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

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