読者です 読者をやめる 読者になる 読者になる

テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】ファイルサイズとメモリ消費を無意味に増やさない為の、AssetBundleと依存関係についてのメモ

今回はAssetBundleとAssetBundleの依存関係についてです。これについては、AssetBundleを使うなら最低限知っといた方が良いかもしれません。

AssetBundleとアセットの依存関係

Unityは非常に複雑な依存関係を持ちやすい構造になっています。例えばアニメーションする2Dキャラクターを表現するのみでも、結構色々なアセットを参照して出来上がっています。
3Dの場合は、これにさらにマテリアルやシェーダーといった物が追加されます。

f:id:tsubaki_t1:20160321223715j:plain

整理してみると結構複雑なAssetBundleですが、AssetBundleでPrefabを構築する際、ここまで考える必要はありません。UnityはAssetBundleを構築する際にAssetBundleが必要なアセットを組み込んでくれる為です。勝手に。
なので、開発者は「PrefabをAssetBundleからロード」するだけで、プレハブが参照しているスクリプトやらアニメーションやらテクスチャやらが自動的にロードされる訳です。

複数のAssetBundleから特定のアセットを参照する場合

ここで一つ問題になってくるのが、「重複したアセットがあった場合どうなるか」です。【Unity】SpritePackerとAssetBundleについて を読んでいたら分かる通り、別々のインスタンスとしてAssetBundle毎に生成されます。

例えば同じTextureを参照しているPrefabを二つ作りAssetBundleAとAssetBundleBにそれぞれ格納すると、各々が単体で動作するようにリソースを格納します。
少し大げさですが、下のような感じです。

f:id:tsubaki_t1:20160321224939j:plain

これはスプライトに限った事では無く、全アセットに言える事ですが、特にTextureのようなメモリ・ファイル的に嵩張る項目が重複して作られた場合、その圧迫行はかなりの物になります。当然、他の要素もそれなりに喰います。

f:id:tsubaki_t1:20160325234336j:plain

この項目はProfilerで確認する事が出来ます。
実際に確認してみると、AssetBundleを2つロードするとテクスチャが2枚分ロードされ、非常に高い多くのメモリを消費している事が分かります。

f:id:tsubaki_t1:20160326000319j:plain

これは、例えAssetbundleがロードするテクスチャをResources等にてUnityのプロジェクトに含めた場合でも同様です。

リソースは別のAssetBundleに格納して、それを読む

AssetBundle毎にインスタンスを保持する問題を解決するには、単一のAssetBundleにアセットを持たせ、それを参照する形でAssetBundleを読み込む方法が有効です。

http://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20160309/20160309235521.jpg

この形でアセットを読む場合、各々のAssetBundleがアセットを含む訳では無いので、メモリの消費量はテクスチャ1枚分で済みます

またダウンロードサイズ的にも、複数のAssetBundleに含まれていたリソースを集約する事で削減が見込めます。

例えば、下の比較はスプライトとAnimationを持たせたAssetBundleの、分割後と分割前の比較です。AssetBundleを分割する前の合計サイズは1369kbですが、分割後は827kbと、ファイルサイズが半分近くになっています。

f:id:tsubaki_t1:20160326002008j:plain

依存関係のあるAssetBundleを作る

依存関係のあるAssetBundleの作り方ですが、参照先のリソースを別途AssetBundleに格納するだけです。
これだけで、AssetBundleを構築する際によしなにしてくれます。また、AssetBundleを読み込んだ際、同時に依存先のAssetBundleが展開されている場合、自動的にAssetBundleの依存関係を解決してリソースを読んでくれます。

例えば、今回はキャラクターをロードするのに必要なリソース…例えばSpriteやTextureやAnimationClipやAnimationOverrideControllerを、単体のAssetBundleに全て格納しました。

f:id:tsubaki_t1:20160326002637j:plain

これでPrefabやSceneを展開した際、AssetBundleがロードされていれば自動的に依存関係に従いリソースを取得してくれます。また、AssetBundleManagerを使えば、依存関係の解決もいい感じにしてくれます

f:id:tsubaki_t1:20160326004504j:plain

 

依存関係はAssetBundle Manifestから確認出来ます。

f:id:tsubaki_t1:20160326005020j:plain

ちなみに、AssetBundleの依存関係の構築は「同一のプロジェクト内でのみ」機能するみたいです。なので、別プロジェクトで構築したAssetBundleへ自動的に依存するといった事は無理そうです。

GUIDが同一なら別プロジェクトで作っても問題なく読めます。

依存先のAssetBundleは差し替えられる

このギミックは、テクスチャの一部やプレハブの一部を更新したい時に大いに便利です。
例えばPrefabのパラメータのみを更新したい場合、全てのAssetBundleを更新せずPrefabを含めるAssetBundleだけ更新すれば良いです。同様にテクスチャやAnimationControllerのみを変更したい場合、それを含めるAssetBundleだけ更新すれば良いです。

f:id:tsubaki_t1:20160326021738j:plainただシーンからPrefabへ参照するタイプのAssetBundleだけは、PrefabのAssetBundleを更新してもSceneをロードした際に中身を差し替えてくれませんでした
これは多分、Prefabの情報はSceneに直で書き込まれるという事なんじゃないかなと思います。

シーン内のPrefabだけを更新したい場合だけは、Prefabをロードするコードをシーン起動時に呼ぶのが良さそうです

依存関係のあるAssetBundleをロードする際の注意

多分AssetBundleの展開はfile openと同じ物なので、同時に開ける数に限界があるかもしれません。この数はOSに依存すると思いますiOSで8個もしくは78個、250個らへんのどれかかと。

調査したらOpen限界は特に無かったです。但し一つにつき数キロバイトのメモリを喰うので、膨大な量を常時オープンは無理です(モバイルだと)。

CacheOrDownload等でキャッシュする際にはIO書込を行うので、アクセスに限界数があります。7とかその辺り(OSやエンジンが使用してる事もあるので、実際はもっと少ない)

 

また依存の解決は「開いているAssetBundle」に対してのみ有効みたいです。つまり、スプライト・プレハブ・アニメーションとAssetBundleをLoad->Unloadを繰り返すような運用を行う場合、依存関係を構築出来ないみたいです。

f:id:tsubaki_t1:20160326025352j:plain

感想

この「依存関係を構築するルール」、見直すと結構シンプルな仕組みではありますが、知らないと結構色々な所でメモリサイズやファイルサイズを増やす事になりそうです。

またUnity 5.0から依存関係をかなり簡単に使えるようになりましたが、条件が「ABひらきっぱ」なので場合によっては(特にメモリにABを載せる場合)は、Unity 4時代と同じように自前で並べるようなコードを頑張って書くのも良いかもしれません。

関連情報

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp