この項では、アセットバンドルを使用したプロジェクトでよくある問題をいくつか解説しています。
Unity 5 のアセットバンドル システムは、オブジェクトがアセットバンドルにビルドされる際に、そのオブジェクトの全ての依存を特定します。これは Asset Database によって行われます。この依存情報は、そのアセットバンドルに含まれることになるオブジェクトの一式を特定するために使用されます。
あるアセットバンドルに明示的に(手動で)割り当てられたオブジェクトは、そのアセットバンドル内にのみビルドされます。「明示的に割り当てらた」オブジェクトとは、 AssetImporter の assetBundleName が空でない文字列に適切に設定されているオブジェクトです。
アセットバンドルに手動で割り当てられていないオブジェクトは、タグ無しオブジェクトを参照するオブジェクトを 1 つ以上含む全てのアセットバンドルに含められます。
2 つの異なるオブジェクトが 2 つの異なるアセットバンドルに割り当てられていて、その両方が共通の依存オブジェクトへの参照を持っている場合、その依存オブジェクトは両方のアセットバンドル内にコピーされます。コピーされた依存もインスタンス化されるので、重複した 2 つの依存オブジェクトが、異なる識別子を持つ異なるオブジェクトとして認識されます。このため、アプリケーションのアセットバンドルの合計サイズが増加します。また、アプリケーションがその重複した 2 つのオブジェクト両方の親を読み込んだ場合、両方のオブジェクトが重複してメモリに読み込まれることになります。
この問題の解決方法はいくつかあります。
異なるアセットバンドル内にビルドされた複数のオブジェクトが依存を共有しないようにする。依存を共有するオブジェクトは、その依存を重複させることなく同じアセットバンドル内に配置することができます。
同じ依存を共有する複数のアセットバンドルが同時に読み込まれないようにアセットバンドルを分類する。
全ての依存アセットが、その所属するアセットバンドル内に確実にビルドされるようにする。これによりアセットの重複が起きる可能性は無くなりますが、複雑な状況が発生します。アプリケーションは、異なるアセットバンドル間における依存をトラッキングし、 AssetBundle.LoadAsset APIs の呼び出しが行われる前に正しいアセットバンドルが確実に読み込まれようにしなければなりません。
Unity 5 では、オブジェクトの依存は UnityEditor 名前空間にある AssetDatabase API によってトラッキングされます。この名前空間が示す通り、この API は Unity Editor のみで利用可能であり、ランタイムでは利用できません。 AssetDatabase.GetDependencies は、特定のオブジェクトやアセットの全ての直接依存を特定するのに使用できます。ここで、依存自体が依存を持っている場合もあることにご注意ください。また、 AssetImporter API を使用して、どのオブジェクトでも割り当てられるアセットバンドルをクエリーすることができます。
AssetDatabase と AssetImporter API を組み合われば、特定のアセットバンドルの全ての直接依存と間接依存が確実にアセットバンドルに割り当てられるようにするエディタースクリプトを書くことも可能です。あるいは、どのアセットバンドルにも割り当てられていない依存が複数のアセットバンドルによって共有されないようにするエディタースクリプトを書くこともできます。アセット重複によるメモリコストを考慮すると、そのようなスクリプトの導入は全てのプロジェクトに推奨されます。
以下のセクションでは、 Unity 5 で、アセット依存の計算コードを自動生成のスプライトアトラスと併用した場合に起こる特異な現象について解説します。
自動生成されたスプライト アトラスは全て、その生成元のスプライト オブジェクトが含まれるアセットバンドルに割り当てられます。スプライト オブジェクトが複数のアセットバンドルに割り当てられている場合、そのスプライト アトラスは単一のアセットバンドルに割り当てられるのではなく、複製されます。スプライト オブジェクトがどのアセットバンドルにも割り当てられていない場合、そのスプライト アトラスも、どのアセットバンドルにも割り当てられません。
スプライト アトラスの重複を確実に防ぐためには、同じスプライト アトラス内にタグ付けされた全てのスプライトが、確実に同じアセットバンドルに割り当てられるようにしてください。
Unity 5.2.2p3 またはそれ以前のバージョンの場合
自動生成されたスプライト アトラスはアセットバンドルに割り当てられることはありません。このため、自動生成されたスプライト アトラスは、それを構成するスプライトの含まれるアセットバンドルと、そのスプライトを参照するアセットバンドルのどれにでも含められます。
上記の問題があるため、Unity 5 のプロジェクトで Unity のスプライト パッカーを使用している場合は、 Unity 5.2.2p4 か 5.3、またはそれ以降のバージョンへアップグレードされることが強く推奨されます。
アップグレードが不可能なプロジェクトの場合は、以下の 2 つの方法でこの問題を回避することができます。
【難易度・低】 Unity ビルトインのスプライト パッカーの使用を避ける。外部ツールによって生成されたスプライト アトラスは通常のアセットとなり、正常にアセットバンドルに割り当てることができます。
【難易度・高】 自動でアトラス化されたスプライトを含む全てのオブジェクトを、スプライトとして同じアセットバンドルに割り当てる。
これにより、生成されたスプライト アトラスが他のアセットバンドルの間接依存として扱われなくなり、重複が起こらなくなります。
この解決方法だとワークフローは Unity のスプライト パッカーを使用した場合と同じですが、開発者にとっては、アセットを異なるアセットバンドルに分けることがより困難になり、該当のアトラスを参照するコンポーネントのどれかに何らかのデータ変更があった場合に、たとえそのアトラス自体には変更がなかったとしても、スプライト アトラス全体の再ダウンロードが強制されることとなります。
Android のエコシステムではデバイスが極度に断片化されるため、テクスチャをいくつかの異なる形式に圧縮する必要が頻繁に生じます。全ての Android デバイスは ETC1 に対応していますが、 ETC1 はアルファチャンネルを持ったテクスチャには対応していません。 OpenGL ES 2 に対応する必要のないアプリケーションの場合、問題を解決する最もシンプルな方法は、ETC2 を使用することです。 ETC2 には全ての Android OpenGL ES 3 が対応しています。
ほとんどのアプリケーションでは、 ETC2 に非対応の古いデバイスへの配信が必要になります。この問題を解決するひとつの方法は、 Unity 5 の AssetBundle Variant の使用です。(その他の方法についての詳細は、Unity の Android 最適化ガイドをご参照ください。)
AssetBundle Variant を使用するには、 ETC1 を使用してクリーンに圧縮できないテクスチャは全て、テクスチャ専用のアセットバンドル内に分離する必要があります。次に、 DXT5 や PVRTC、ATITC などの、vendor-specificのテクスチャ圧縮形式を使用して、Android エコシステムにおける ETC2 非対応のスライスに対応するために、これらのアセットバンドルのバリアントを十分量作成します。それぞれの AssetBundle Variant に関して、それに含まれるテクスチャの TextureImporter 設定を、そのバリアントに適した圧縮形式に変更してください。
ランタイムでは、各種テクスチャ圧縮形式への対応は SystemInfo.SupportsTextureFormat API で判定できます。この情報は、対応された形式で圧縮されたテクスチャを含む AssetBundle Variant の選択と読み込みに使用されます。
Android のテクスチャ圧縮形式についての更なる情報はこちらでご確認いただけます。
以下のセクションで解説されている問題は Unity 5.3.2p2 で解決されています。最新バージョンの Unity ではこの問題は起こりません。
5.3.2p2 より古いバージョンの Unity では、アセットバンドルの読み込み中はずっと、そのアセットバンドルへのオープンファイルハンドルが保持されていました。これは大部分のプラットフォームでは問題ありませんが、 iOS では、ひとつの処理で同時にオープンにできるファイルハンドルの数が 255 までに制限されています。アセットバンドルの読み込みによってこの制限を超過した場合、 “Too Many Open File Handles” (「オープンファイルハンドル過多」)のエラーが出て読み込みコールが失敗に終わります。
これは、コンテンツを数百、数千のアセットバンドルに分割しているプロジェクトにおいて多く見られる問題でした。
パッチが適用されたバージョンの Unity にアップグレードできないプロジェクトの場合、一時的な解決策は以下の通りです。