AssetBundleのベストプラクティス

##四、AssetBundleのベストプラクティス

 
前のセクションでは、AssetBundleの基本原則を紹介し、AssetBundleのロードからAssetsのロードまでのさまざまなプロセスと、基盤となるAPI実装の詳細について説明しました。このセクションでは、AssetBundleの実際の使用に当たった問題と解決策ついて説明します。
 
###4.1ロードされたAssetsを管理する

 
一部のメモリが敏感な環境では、ロードされるオブジェクトのサイズと数を厳密に制御することが重要です。オブジェクトがアクティブなシーンから削除されても、Unityはオブジェクトを自動的にアンロードしません。Assetsのクリーンアップは特定の時間にトリガーされます。もちろん、手動でトリガーすることもできます（実際にはGCです）。
 
AssetBundleは慎重に管理する必要があります。ローカルストレージ内のファイル（UnityキャッシュまたはAssetBundle.LoadFromFileを介してロードされたファイル）でサポートされるAssetBundleのメモリコストは最小限で、数十Kバイトを超えることはめったにありません。ただし、AssetBundleが多数ある場合でも、このコストは問題になります。
 
ほとんどのプロジェクトでは、ゲーマーがゲーム（あるステージをリプレイするなど）を繰り返し体験できると承認します。そのため、　AssetBundleをいつロードまたはアンロードするかを知ることが非常に重要になります。AssetBundleが適切にアンロードされていない場合、メモリ内のオブジェクトの冗長コピーが発生します。場合によっては、AssetBundleを不適切にアンロードすると、テクスチャの損失などの望ましくない動作が発生する可能性もあります。
 
AssetsとAssetBundlesを管理する場合、最も重要なポイントは、AssetBundle.unloadが呼び出される方法です。unloadパラメーターはtrueまたはfalseです。
 
このAPIは、呼び出されているAssetBundleのヘッダー情報をアンロードします。Unloadのパラメータは、このAssetBundleからインスタンス化されたすべてのオブジェクトもアンロードするかどうかを決定します。trueに設定すると、アクティブシーンで現在参照されている場合でも、AssetBundleから作成されたすべてのオブジェクトもすぐにアンロードされます。
 
たとえば、Material MがAssetBundle ABからロードされ、Mが現在アクティブなシーンにあるとします。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/a82cf167-1fc0-1481-5c27-5f1f84526af7.png)


 
 
AssetBundle.Unload（True）が呼び出されると、Mはシーンから削除され、破棄されてアンロードされます。ただし、AssetBundle.Unload（False）が呼び出されると、ABのヘッダー情報はアンロードされますが、Mはシーンに残り、引き続き使用できます。AssetBundle.Unload（False）を呼び出すと、MとABの間のリンクが切断されます。後でABが再度ロードされると、ABに含まれるオブジェクトの新しいコピーがメモリにロードされます。
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/c766e808-aa99-054b-acb6-808649f02c11.png)


 
 
後でABが再度ロードされると、AssetBundleのヘッダー情報の新しいコピーが再ロードされます。ただし、MはこのABの新しいコピーからロードされません。 Unityは、ABとMの新しいコピーの間に接続を確立しませんでした。
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e682b92b-1c85-d350-3bfd-e51ff62ca2c1.png)


 
 
AssetBundle.LoadAsset（）を呼び出してMをリロードすると、UnityはMの古いコピーをABのデータのインスタンスとして使用しません。したがって、UnityはMの新しいコピーをロードするため、この時点でMの2つの同一のコピーが現場にあります。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/e26ed529-3c13-5f4e-5d78-ec29bdfc1853.png)


 
 
ほとんどのプロジェクトでは、このような結果は望ましくありません。ほとんどのプロジェクトでは、AssetBundle.Unload（True）を使用し、オブジェクトがコピーされないようにする必要があります。 一般的に、次のような両種類の方法があります。
 
>（1）アプリケーションのライフサイクル中に適切なノードを定義し（レベルの切り替えや画面をロードする時など）、この期間中に不要なAssetBundleをアンロードします。これは最も単純で最も一般的な選択です。
（2）単一のオブジェクトの参照計数を維持し、すべての構成オブジェクトが使用されていない場合にのみAssetBundleをアンインストールします。これにより、アプリケーションはメモリを繰り返すことなく、個々のオブジェクトをアンロードおよびリロードできます。
 
アプリケーションがAssetBundle.Unload（False）を使用する必要がある場合、以下のような両種類の方法で各オブジェクトをアンロードします。
 
>（1）シーンおよびコード内の不要なオブジェクトへのすべての参照を削除します。終了したら、Resources.UnusedAssetを呼び出します。
（2）追加以外の読み込みシーン。これにより、現在のシーンのすべてのオブジェクトが破棄され、Resources.UnusedAssetが呼び出されます。
 
プロジェクトにノードが明確に定義されている場合、プレーヤーは、ゲームモードやレベルの切り替えなど、オブジェクトのロードとアンロードを待つことができます。それに、必要に応じてできるだけ多くのオブジェクトをアンロードし、新しいオブジェクトをロードできます。
 
最も簡単な方法は、プロジェクトの個別のブロックをシーンにパッケージ化し、これらのシーンをすべての依存関係とともにAssetBundleにビルドすることです。アプリケーションは、「loading」シーンに切り替えて、古いシーンを含むAssetBundlesを完全にアンロードしてから、新しいシーンを含むAssetBundlesをロードできます。
 
これは最も単純なプロセスですが、複雑なAssetBundles管理が必要です。プロジェクトはそれぞれ異なるため、Unityはまだ共通のAssetBundlesデザインパターンを提供していません。
 
オブジェクトをAssetBundleにグループ化する方法を決定するときに、オブジェクトを同時にロードまたは更新する必要がある場合は、通常、オブジェクトをAssetBundleにバンドルするのが最適です。たとえば、ロールプレイングゲームについて考えてみます。個々のマップとトリミングされたシーンは、シーンごとにAssetBundlesにグループ化できますが、一部のオブジェクトは他の多くのシーンに存在する可能性があり、グループに入れることはできません。
 
AssetBundlesを使用して、ポートレート、ゲーム内UI、およびさまざまなキャラクターモデルとテクスチャを提供できます。これらの後で必要なObjectsとAssetsは、AssetBundleの2番目のグループにグループ化できます。これらのAssetsは起動時にロードされ、アプリケーションのライフサイクルの間ロード状態を保持します。
 
AssetBundleがアンロードされた後にUnityがAssetBundleからオブジェクトをリロードする必要がある場合、別の問題が発生する可能性があります。この場合、リロードは失敗し、オブジェクトはUnityエディターの階層に（Missing）オブジェクトとして表示されます。
 
これは主に、モバイルアプリケーションが一時停止されている場合やユーザーがPCをロックしている場合など、Unityがグラフィックスコンテキストの制御を失って取り戻そうとした場合に発生します。この場合、UnityはテクスチャとレンダリングをGPUに再アップロードする必要があります。これらのAssetのソースAssetBundleが利用できない場合、アプリケーションはシーン内の関連オブジェクトをマゼンタにして表示します。
 
 
###4.2リリース

 
クライアントがプロジェクトのAssetBundleを公開するには、2つの基本的な方法があります。**プロジェクトと一緒にインストールするか、インストール後にダウンロードするかの方法です。**
 
インストール時にまたはその後にAssetBundlesを渡すかは、プロジェクトが実行されるプラットフォームの機能と制限によって異なります。モバイルプロジェクトは通常、インストールしてからダウンロードすることを選択します。これにより、初期インストールサイズが減らされ、関連するプラットフォームのダウンロードサイズ制限未満に保ちられます（たとえば、Apple StoreとGoogleストアは4Gモードでダウンロード可能な最大パッケージを制限します）。コンソールおよびPCプロジェクトには、通常、初期インストール時にAssetBundleが付属しています。
 
優れたアーキテクチャでは、AssetBundleが最初にリリースされた方法に関係なく、インストール後にプロジェクトがアセットのホットアップデートを実行できるようにする必要があります。詳細については、[「Unityマニュアル」](https://docs.unity3d.com/Manual/AssetBundles-Patching.html?_ga=2.161080382.2009813986.1571904029-1030292064.1564583003)の「Patching with AssetBundles」セクションを参照してください。
 
####4.2.1プロジェクトとともにリリース

 
ダウンロードして管理するために追加のコードを必要としないため、プロジェクトでAssetBundleを公開するのが最も簡単です。プロジェクトがインストールされるうちにAssetBundleを含む理由として、主に以下の2つの理由があります。
 
>（1）プロジェクトのビルド時間を短縮し、より簡単な反復型開発を可能にします。これらのAssetBundleをアプリケーション自体とは別に更新する必要がない場合は、AssetBundleをStreaming Assetsに保存することで、AssetBundleをアプリケーションに含めることができます。以下の「Streaming Assets」セクションを参照してください。
（２）更新可能なコンテンツの初期リビジョンを公開します。これは通常、最初のインストール後、または将来のパッチ適用の基礎として、エンドユーザーの時間を節約するためです。この状況では、Streaming Assetsは理想的ではありません。ただし、カスタムのダウンロードおよびキャッシュシステムを作成することを選択しない場合は、更新可能なコンテンツの初期リビジョンをStreaming AssetsからUnityキャッシュにロードできます（以下のキャッシュの起動セクションを参照）。
 
**（1）Streaming Assets**
 
インストール中にUnityアプリケーションに任意のタイプのコンテンツ（AssetBundleを含む）を含める最も簡単な方法は、プロジェクトをビルドする前にコンテンツを/ Asset / StreamingAsset /フォルダーにビルドすることです。ビルド時にStreamingAssetフォルダーに含まれるコンテンツはすべて、最終的なアプリケーションにコピーされます。
 
実行時に、プロパティApplication.StreamingAssetsを介してローカルストレージ上のStreamingAssetフォルダーのフルパスにアクセスできます。次に、AssetBundlesは、AssetBundle.LoadFromFileを介してほとんどのプラットフォームにロードできます。
 
Android開発者：Androidでは、StreamingAssetフォルダー内のAssetはAPKに保存されています。圧縮されている場合、APKに保存されているファイルは異なるストレージアルゴリズムを使用できるため、読み込みに時間がかかる場合があります。また、使用されるアルゴリズムは、Unityのバージョンによっても異なる場合があります。
 
アーカイブプログラム（7-zipなど）を使用してAPKを開き、これらのファイルが圧縮されているかどうかを確認できます。そうである場合、AssetBundle.LoadFromFile（）の実行が遅くなります。
 
UnityWebRequest.GetAssetBundleをソリューションとして、キャッシュされたバージョンを検索できます。 UnityWebRequestを使用することにより、AssetBundleは最初の実行時に解凍およびキャッシュされ、その後の実行が高速になります。ただし、AssetBundleがキャッシュにコピーされるため、これにより多くのストレージスペースが必要になることに注意してください。または、Gradleプロジェクトをエクスポートし、ビルド時にAssetBundlesにextensionを追加します。次に、build.gradleファイルを編集して、noCompressセクションにextensionを追加できます。完了後、解凍のパフォーマンスコストを消費することなく、AssetBundle.LoadFromFile（）を直接使用できます。
 
注：一部のプラットフォームでは、StreamingAssetは読み取り専用です。インストール後にプロジェクトのAssetBundleを更新する必要がある場合は、WWW.LoadFromCacheOrDownloadを使用するか、カスタムダウンロードプログラムを作成してください。
 
####4.2.2インストール後のダウンロード

 
AssetBundleをモバイルデバイスに配信する最良の方法は、アプリのインストール後にダウンロードすることです。これにより、ユーザーがアプリケーション全体を再ダウンロードしなくても、インストール後にゲームコンテンツを更新できます。多くのプラットフォームでは、アプリケーションバイナリは、費用と時間がかかる再認証プロセスを経る必要があります。したがって、優れた個別のダウンロードシステムを開発することが不可欠です。
 
AssetBundleを配信する最も簡単な方法は、それらをWebサーバーに配置し、UnityWebRequestを介して公開することです。 Unityは、ダウンロードしたAssetBundleをローカルストレージに自動的にキャッシュします。ダウンロードしたAssetBundleがLZMAで圧縮されている場合、AssetBundleは、将来の読み込みを高速化するために、LZ 4（Caching.compressionEnabled設定に依存する）と同様に、非圧縮または再圧縮された形式でキャッシュに保存されます。ダウンロードしたパッケージがLZ4で圧縮されている場合、AssetBundlesは圧縮されて保存されます。キャッシュがいっぱいになると、Unityは最近にあまり使用されていないAssetBundleをキャッシュから削除します。
通常、許す限りUnityWebRequestを使用することをお勧めします。Unity5.2以前を使用している場合しかにWWW.LoadFromCacheOrDownloadを使用しないことをお勧めします。内蔵APIのメモリコスト、キャッシュ動作、またはパフォーマンスが特定のプロジェクトで受け入れられない場合、またはプロジェクトがそのニーズを満たすためにプラットフォーム固有のコードを実行しなければならない場合にのみ、カスタムダウンロードシステムを拡張する必要があります。
 
UnityWebRequestまたはWWW.LoadFromCacheOrDownloadの使用を妨げる可能性のある状況の例：
 
(1)AssetBundleキャッシュの粒度を制御する必要がある場合。
(2)プロジェクトがカスタム圧縮策を実装する必要がある場合。
(3)プロジェクトがプラットフォーム固有のAPIを使用して、非アクティブ時にストリーミングデータをロードするなど、特定の要件を満たす場合。たとえば、iOSのバックグラウンドタスクAPIを使用してデータをダウンロードします。
(4)SSLサポート条件がないプラットフォーム（PCなど）でAssetBundleをSSL経由で配信する必要がある場合。
 
####4.2.3キャッシュをビルドする

 Unityには内蔵のAssetBundleキャッシュシステムがあります。これはUnityWebRequest APIを介してダウンロードされたAssetBundleをキャッシュするために使用できます。このAPIをリロードすると、AssetBundleのバージョン番号がパラメーターとして受け入れられます。この番号はAssetBundlesに保存されておらず、AssetBundlesシステムによって生成されていません。
 
キャッシュシステムは、UnityWebRequestに渡された最後のバージョン番号を追跡します。バージョン番号を使用してこのAPIを呼び出す場合、キャッシュシステムは、バージョン番号を比較することにより、キャッシュされたAssetBundleがあるかどうかを確認します。これらの数値が一致する場合、システムはキャッシュされたAssetBundleをロードします。番号が一致しない場合、またはキャッシュされたAssetBundleがない場合、Unityは新しいコピーをダウンロードします。この新しいコピーは、新しいバージョン番号に関連付けられます。
 
キャッシングシステムのAssetBundleは、完全なURLをダウンロードすることではなく、ファイル名によってのみ識別されます。これは、同じファイル名のAssetBundleをCDNなどの複数の異なる場所に保存できることを意味します。ファイル名が同じである限り、キャッシングシステムはそれらを同じAssetBundleとして認識します。
 
各アプリケーションは、AssetBundleにバージョン番号を割り当てる際の考慮事項を決定してから、これらの番号をUnityWebRequestに渡す必要があります。番号は、crc値などの唯一の識別子に由来する場合があります。 AssetBundleManifest.GetAssetBundleHash（）もこの目的で使用できますが、実際の計算ではなく推定値のみを提供するため、バージョン管理にこの関数を使用することはお勧めしません。
 
詳細については、[「Unityマニュアル」](https://docs.unity3d.com/Manual/AssetBundles-Patching.html?_ga=2.152096058.2009813986.1571904029-1030292064.1564583003)の「Patching with AssetBundles」セクションを参照してください。
 
Unity 2017.1では、開発者は複数のキャッシュからアクティブなキャッシュを選択でき、キャッシュAPIがよりきめ細かい粒度の制御が可能になると発展してきました。以前のバージョンのUnityは、Caching.expenationDelayとCaching.AvailableDiskSpaceのみを変更して、キャッシュを削除できます（これらのプロパティは、CacheクラスのUnity2017.1に保持されています）。
 
ExpirationDelayは、AssetBundleが自動的に削除されるまでに経過必須な最小秒数です。この期間中にAssetBundleにアクセスしなかった場合は、自動的に削除されます。
 
MaximumAvailableDiskSpaceは、キャッシュが最後に使用されたAssetBundleの削除を開始する前に使用できるローカルストレージスペースの量（バイト単位）を指定します。制限に達すると、Unityはキャッシュ内で最後に開いたAssetBundleを削除します（またはCaching.MarkAsUsedを介してマークを付けます）。 Unityは、新しいダウンロードを完了するのに十分なスペースができるまで、キャッシュされたAssetBundleを削除し続きます。
 
**（1）Cacheの基本**
 
AssetBundleはファイル名で識別されるため、アプリケーションに付属のAssetBundleを使用してキャッシュを「初期化」できます。このため、各AssetBundleの初期バージョンまたは基本バージョンは/ Asset / StreamingAsset /に保存されます。このプロセスは、上記の「プロジェクトとともにリリース」と同じです。
 
アプリケーションを初めて実行するときは、Application.streamingAssetsPathからAssetBundlesをロードすることでキャッシュを埋めることができます。それ以降、アプリケーションは通常どおりUnityWebRequestを呼び出すことができます（UnityWebRequestは、StreamingAssetパスからAssetBundleを最初にロードする時にも用いられます。）。
 
####4.2.4カスタムダウンローダー

 
カスタムダウンローダーを作成すると、アプリケーションはAssetBundleのダウンロード、解凍、保存の方法を完全に制御できます。関係するエンジニアリング作業は単純ではないため、この方法は大規模なチームにのみお勧めします。カスタムダウンロードプログラムを作成する際の主な考慮事項は次の4つです。
 
>(1)ダウンロードメカニズム
(2)ストレージの場所
(3)圧縮タイプ
(4)パッチ
 
Patching AssetBundleについては、「Unityマニュアル」の「Patching with AssetBundles」セクションを参照してください。
 
**（1）ダウンロード**
 
ほとんどのアプリケーションでは、HTTPがAssetBundleをダウンロードする最も簡単な方法です。ただし、HTTPベースのダウンローダーの実装は簡単な作業ではありません。カスタムダウンローダーは、過度のメモリ割り当て、過度のスレッド使用、および過度のスレッドウェイクアップを回避する必要があります。前のセクションのWWW.LoadFromCacheOrDownloadセクションで詳細に説明されている理由から判断すると、UnityのWWWクラスは不適切です。
 
カスタムダウンロードプログラムを作成する場合、次の3つのオプションがあります。
 
>C＃のHttpWebRequestクラスとWebClientクラス
ローカルプラグインをカスタマイズする
Assetストレージパッケージ
 
**C＃クラス**
 
アプリケーションがHTTPS / SSLサポートを必要としない場合、C＃のWebClientクラスは、AssetBundleをダウンロードするための最も簡単なメカニズムを提供でき、過度のマネージメモリ割り当てなしで、任意のファイルをローカルストレージに直接非同期でダウンロードできます。
 
WebClientでAssetBundleをダウンロードするには、クラスのインスタンスを割り当て、AssetBundleのURLをそのインスタンスに渡して、ダウンロードとターゲットパスを指定してください。リクエストパラメータをさらに制御する必要がある場合は、C＃HttpWebRequestクラスを使用してダウンローダーを作成できます。
 
>(1)HttpWebResponse.GetResponseStreamからバイトストリームを取得します。
(2)スタックに固定サイズのバイトバッファを割り当てます。
(3)応答ストリームからバッファーに読み取ります。
(4)C＃のFile.IOAPIまたはその他のストリーミングIOシステムを使用して、バッファーをディスクに書き込みます。
 
**Asset Storeプラグイン**
 
一部のストアのAssetプラグインは、HTTP、HTTPS、およびその他のプロトコルを介してファイルをダウンロードするためのアンマネージコードに基づく実装を提供します。Unity用のカスタムネイティブコードプラグインを作成する前に、利用可能なAssetStoreプラグインを評価することをお勧めします。
 
**カスタムネイティブプラグイン**
 
カスタムネイティブプラグインの作成は最も時間がかかりますが、Unityがデータをダウンロードするための最も柔軟な方法です。プログラミング時間の要件が高く、技術的なリスクが高いため、この方法は、アプリケーションのニーズを満たすことができる方法が他にない場合にのみ推奨されます。たとえば、アプリケーションがUnityでC＃SSLをサポートしていないプラットフォームでSSL通信を使用する必要がある場合は、ネイティブプラグインをカスタマイズする必要があります。
 
**（2）保管**
 
すべてのプラットフォームで、Application.PersistentDataPathは、アプリケーションの実行間で永続化する必要のあるデータを格納するために、使用できる書き込み可能な場所を指します。カスタムダウンロードプログラムを作成するときは、Application.PersistentDataPathのサブディレクトリを使用してダウンロードしたデータを保存することを強くお勧めします。
 
Application.streamingAssetPathの場所は書き込み可能ではありません。これは、AssetBundleキャッシングにとって厄介な問題です。 StreamingAssetPathの場所の例には次のものがあります：
 
>(1)OSX：.appパッケージ内。書き込み不可
(2)Windows：インストールディレクトリ（プログラムファイルなど）。通常は書き込みできません。
(3)iOS：.ipaパッケージ内。書き込み不可
(4)Android：.apkファイル内。書き込み不可
 
###4.3アセット配置の策略

 
プロジェクトのAssetトをAssetBundleに分割するのは複雑です。 ただし、すべてのオブジェクトを独自のAssetBundleに配置したり、1つのAssetBundleのみを使用したりするなど、単純な策略を採用することは魅力的ですが、このソリューションには大きな欠点があります。
 
>(1)AssetBundleが少なすぎます
(2)実行時のメモリ使用量を増やす
(3)読み込み時間を長くする
(4)さらにダウンロードが必要
(5)AssetBundleの組み合わせが多すぎます
(6)ビルド時間を増やす
(7)複雑な開発
(8)合計ダウンロード時間を増やす
 
したがって、最も重要なことは、オブジェクトをAssetBundleにグループ化する方法です。 主な策略は次のとおりです。
 
>(1)論理エンティティ
(2)オブジェクトタイプ
(3)同時コンテンツ
 
これらのグループ化戦略の詳細については、「マニュアル」を参照してください。
 
###4.4よく見られる落とし穴

 
このセクションでは、AssetBundleを使用するプロジェクトでよくあるいくつかの問題について説明します。
 
####4.4.1Assetの冗長性

 
Unity 5のAssetBundleシステムは、ObjectsがAssetBundleにビルドされると、すべての依存関係を検出します。この依存関係情報は、AssetBundlesに含まれるObjects集を確認するために使用されます。
 
さらに、この情報をAssetBundleにパッケージ化します。
 
AssetBundleに明示的に割り当てられたObjectsは、AssetBundleのみにビルドできます。ObjectsのAssetImporterがそのAssetBundleNameプロパティを空でない文字列に設定すると、「明示的に割り当てられ」ます。これは、ObjectsインスペクターでAssetBundleを選択することで実行できます。またはEditorスクリプトから行うこともできます。
 
Objectsは、AssetBundleビルドマップの一部として定義することにより、AssetBundleに割り当てることもできます。このマップは、リロードされたBuildPiine.BuildAssetBundles（）関数と一緒に使用し、AssetBundleBuild配列を受け入れます。
 
AssetBundleで明示的に割り当てられていないObjectsは、他の1つ以上のマークされていないAssetBundleに含まれます。
 
たとえば、2つの異なるObjectsが2つの異なるAssetBundleに割り当てられているが、両方に共通の依存Objectsへの参照がある場合、依存オブジェクトは2つのAssetBundleにコピーされます。重複する依存関係もインスタンス化されます。つまり、依存関係のあるオブジェクトの2つのコピーは、異なる識別子を持つ異なるObjectsとして扱われます。これにより、アプリケーションのAssetBundleの合計サイズが大きくなります。アプリケーションがこれら2つのオブジェクトを同時にロードする場合、依存オブジェクトは両側にロードされ、メモリに保存されます。
 
この問題を解決するには、いくつかの方法があります。
 
>（1）異なるAssetBundleで構築されたObjectsが依存関係を共有しないことを確認します。依存関係を共有するObjectsは、依存関係のコピーを作成しなくても、同じAssetBundleに配置できます。多くの共有依存関係を持つプロジェクトの場合、この方法は通常実行できません。大量および少量のAssetBundleが発生する可能性があります。更新する場合は、頻繁に再構築または再ダウンロードする必要があります。
（2）依存関係を共有する2つのAssetBundleが同時にロードされないように、AssetBundleを分類します。この方法は、レベルベースのゲームなど、特定の種類のプロジェクトに適している場合があります。ただし、それでもプロジェクトのAssetBundleのサイズが不必要に大きくなり、ビルド時間とロード時間が長くなります。
（3）すべての依存Assetsが独自のAssetsに組み込まれていることを確認します。これにより、冗長な資産のリスクが完全に排除されますが、複雑さももたらされます。アプリケーションは、AssetBundle間の依存関係を追跡し、AssetBundle.LoadAssetAPIを呼び出す前に正しいAssetBundleがロードされていることを確認する必要があります。
 
オブジェクトの依存関係は、UnityEditor命名空間にあるAssetDatabaseAPIを介して追跡できます。名前空間が示すように、このAPIはUnityEditorでのみ使用可能であり、実行時に使用することはできません。GetDependentsを使用して、特定のオブジェクトまたはアセットのすべての直接依存関係を見つけることができます。これらの依存関係には独自の依存関係がある場合があることに注意してください。さらに、AssetImportAPIを使用して、特定のObjectsの AssetBundleを照会して割り当てることもできます。
 
AssetDatabaseとAssetImportAPIを組み合わせることで、エディタースクリプトを記述して、AssetBundleのすべての直接または間接の依存関係がAssetBundleに割り当てられるようにしたり、2つのAssetBundleがAssetBundleに割り当てられていない依存関係を共有しないようにしたりできます。Assetコピーのメモリコストのため、すべてのプロジェクトにそのようなスクリプトを含めることをお勧めします。
 
####4.4.2Sprite atlasの冗長性

 
自動生成されたSpriteアトラスは、Spriteアトラスを生成したSprite Objectsを含むAssetBundleに割り当てられます。Sprite Objectsが複数のAssetBundleに割り当てられている場合、SpriteアトラスはAssetBundleに割り当てられず、コピーが生成されます。Sprite ObjectsがAssetBundleに割り当てられていない場合、SpriteアトラスはAssetBundleに割り当てられません。
 
Spriteアトラスがコピーされないようにするには、同じSpriteアトラスでマークされたすべてのSpriteが同じAssetBundleに割り当てられていることを確認してください。 Unity 5.2.2p3以前のバージョンでは、自動生成されたSpriteアトラスがAssetBundleに割り当てられることはないことに注意してください。したがって、それらは、構成スプライトを含むすべてのAssetBundleと、その構成スプライトを参照するすべてのAssetBundleに含まれます。この問題のため、すべてのUnity5プロジェクトでUnityのSpritePackerを使用してUnity5.2.2p4、5.3、またはそれ以降のバージョンのUnityにアップグレードすることを強くお勧めします。
 
####4.4.3Androidテクスチャ

 
Androidエコシステムではデバイスハードウェアがさまざまに異なるため、通常、テクスチャをいくつかの異なる形式に圧縮する必要があります。すべてのAndroidデバイスはETC1をサポートしていますが、ETC1はalphaチャネルを備えたテクスチャをサポートしていません。アプリケーションがOpenGLES 2のサポートを必要としない場合、最もクリーンな解決策は、すべてのAndroid OpenGL ES3デバイスでサポートされているETC2を使用することです。
 
多くのアプリケーションは、ETC2をサポートしていない古いデバイスでリリースする必要があります。この問題を解決する1つの方法は、Unity5のAssetBundleバリアントを使用することです（他のオプションの詳細については、UnityのAndroid最適化ガイドを参照してください）。
 
AssetBundleバリアントを使用するには、ETC 1を使用して完全に圧縮できないすべてのテクスチャを、テクスチャのみを持つAssetBundleに分離する必要があります。次に、DXT 5、PVRTC、ATITC、などの特定的なベンダーによるテクスチャ圧縮形式を使用して、ETC2機能を持たないAndroidエコシステムのスライスをサポートするのに十分なこれらのAssetBundleのバリアントを作成します。 AssetBundleバリアントごとに、含まれているテクスチャのTextureImporter設定を、そのバリアントに適した圧縮形式に変更する必要があります。
 
実行時に、SystemInfo.SupportsTextureFormatAPIを使用して、さまざまなテクスチャ圧縮形式のサポートを検出できます。この情報はAssetBundleバリアントを選択・ロードすることに応用し、対応する圧縮テクスチャ形式をサポートできます。
 
Androidテクスチャ圧縮形式の詳細については、こちら(中国語注意)をご覧ください。
 
####4.4.4iOSファイルハンドルの一時的な使用

 
Unityの現在のバージョンは、この問題の影響を受けなくなりました。
 
Unity 5.3.2p2より前のバージョンでは、Unityは、AssetBundleがロードされている間、AssetBundleのために開いているファイルハンドルを保存します。ほとんどのプラットフォームでは、これは問題ではありません。ただし、iOSでは、プロセスのファイルハンドルの数を同時に最大255に制限しています。 AssetBundleのロードによってこの制限を超えた場合、ロード呼び出しは「開いているファイルハンドルが多すぎます」というエラーで失敗します。
 
これは、コンテンツを数百または数千のAssetBundleに分割しようとするプロジェクトに共通の問題です。
 
パッチを適用したバージョンにアップグレードできないUnityプロジェクトの場合、一時的な解決策は次のとおりです。
 
>(1)関連するAssetBundleをマージして、使用中のAssetBundleの数を減らします。
(2)AssetBundle.Unload（False）を使用して、AssetBundleのファイルハンドルを閉じ、ロードされたオブジェクトのライフサイクルを手動で管理します。
 
###4.5AssetBundleバリアント

 
AssetBundleシステムの重要な機能は、AssetBundleバリアントの導入です。バリアントの目的は、アプリケーションがランタイム環境により適したコンテンツに調整できるようにすることです。バリアントを使用すると、オブジェクトをロードしてインスタンスID参照を解決するときに、異なるAssetBundleファイル内の異なるUnityEngine.Objectを「同じ」オブジェクトとして表示できます。概念的には、2つのUnityEngine.Objectsが同じFileGU IDとLocal IDを共有しているように見え、文字列バリアントIDを介して実際のUnityEngine.Objectを識別できます。
 
システムには2つの主な使用例があります。
 
（1）バリアントは、特定のプラットフォームに適したAssetBundleのロードを簡素化します。
 
>例：システムをビルドするには、スタンドアロンのDirectX 11 Windowsビルドに適した高解像度のテクスチャと複雑なシェーダーを含むAssetBundleと、コンテンツの忠実度が低くAndroidに適した2番目のAssetBundleを作成する場合があります。実行時に、プロジェクトのアセット読み込みコードは、そのプラットフォームに適切なAssetBundleバリアントを読み込むことができ、AssetBundle.LoadAPIに渡されるオブジェクト名を変更する必要はありません。
 
（2）バリアントを使用すると、アプリケーションは同じプラットフォームに異なるコンテンツをロードできますが、異なるハードウェアを使用できます。
 
>これは、複数のモバイルデバイスをサポートするための鍵です。 iPhone 4は、real-worldのアプリケーションで最新のiPhoneと同じコンテンツの忠実度を保証することはできません。
Androidでは、AssetBundleバリアントを使用して、デバイス間の画面アスペクト比とDPIの大きな違いを解決できます。
 
####4.5.1制限

 
AssetBundleバリアントシステムの主な制限は、バリアントを異なるAssetから構築する必要があることです。これらのAssetアセット間の唯一の変更がインポート設定である場合でも、この制限は妥当です。変数AとバリアントBで構築されたテクスチャの唯一の違いが、Unityテクスチャインポーターで選択された特定のテクスチャ圧縮アルゴリズムである場合、変数AとバリアントBは完全に異なるAssetである必要があります。これは、変数AとバリアントBがディスク上の別々のファイルでなければならないことを意味します。
 
特定のAssetの複数のコピーをソース管理に保持する必要があるため、この制限により大規模なプロジェクトの管理が複雑になります。開発者がAssetの内容を変更したい場合は、Assetのすべてのコピーを更新する必要があります。この問題に対する良い解決策は特にありません。
 
ほとんどのチームは、AssetBundleの独自のバリアントを実装します。これは、特定のAssetBundleによって表される特定のバリアントを識別するために、明確に定義されたサフィックスを使用してAssetBundlesファイル名を作成することによって行われます。カスタムコードは、これらのAssetを構築するときに、Assetのインポート設定をプログラムで変更します。一部の開発者は、カスタムシステムを拡張して、Prefabsに接続されているコンポーネントのパラメーターも変更できるようにしています。
 
###4.6圧縮するか、圧縮しないか？

 
AssetBundleを圧縮するかどうかを検討するために、次のようないくつかの重要な問題があります。
 
>(1)読み込み時間：ローカルストレージまたはローカルキャッシュから読み込む場合、非圧縮のAssetBundleは圧縮されたAssetBundleよりもはるかに高速に読み込まれます。
(2)ビルド時間：ファイルを圧縮する場合、LZMAとLZ 4は非常に遅く、統合エディターはAssetBundleを順番に処理します。大きなアセットBundlesを持つプロジェクトは、それらを圧縮するのに多くの時間を費やします。
(3)アプリケーションサイズ：AssetBundleがアプリケーションで提供されている場合、それらを圧縮すると、アプリケーションの合計サイズが小さくなります。または、インストール後にAssetBundleをダウンロードすることもできます。
(4)メモリ使用量：Unity 5.3より前は、Unityのすべての解凍メカニズムでは、解凍前に圧縮されたAssetBundle全体をメモリにロードする必要がありました。メモリ使用量が大きな場合は、非圧縮またはLZ4圧縮AssetBundleを使用してください。
(5)ダウンロード時間：AssetBundleが大きい場合、またはユーザーが帯域幅に制約のある環境にいる場合（たとえば、低速接続や従量制接続でのダウンロード）にのみ、圧縮が必要になる場合があります。数十メガバイトのデータだけがあれば、高速接続を介してPCに送信される場合、圧縮を無視することができます。
 
####4.6.1Crunch圧縮

 
主にDXT圧縮テクスチャで構成され、Crunch圧縮アルゴリズムを使用するAssetBundleは、非圧縮と見なす方がより妥当だと思われます。
 
 
###4.7AssetBundleとWebGL
 
UnityのWebGLエクスポートオプションは現在ワーカースレッドをサポートしていないため、WebGLプロジェクトでのすべてのAssetBundleの解凍と読み込みはメインスレッドで行う必要があります。XMLHttpRequestを使用して、AssetBundleのダウンロードをブラウザーに委任します。ダウンロードされると、圧縮されたAssetBundleはUnityのメインスレッドで解凍されるため、Unityコンテンツの実行はパッケージのサイズに応じて遅延になります。
 
Unityは、パフォーマンスの問題を回避するために、開発者が小規模なAssetBundleを使用することをお勧めします。大規模なAssetBundleを使用する場合と比較して、この方法はメモリ効率も高くなります。 UnityWebGLはLZ4圧縮および非圧縮AssetBundleのみをサポートしますが、gzip / brotli圧縮はUnityによって生成されたパッケージに適用できます。この場合、ブラウザのダウンロード時にファイルを解凍するようにWebサーバーを配置する必要があります。詳細については、こちらをクリックしてください。
 
Unity 5.5以前を使用している場合は、AssetBundleにLZMAを使用せず、圧縮にLZ 4を使用することを検討してください。これは、必要に応じる解凍に非常に効果的です。Unity 5.6は、WebGLプラットフォームの圧縮オプションとするLZMAを削除しました。

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com