テラシュールブログ

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

【Unity】複数のAssetBundleから参照されるアセットを見つけ出す

AssetBundleにて「AssetBundle一つにつきPrefabを一つ格納」といった感じで設計している場合、一見Prefabのアセットのみが格納されているように見えますが、実際は「Prefabが参照しているすべてのアセット」が含まれます。

といった内容が以前書いた【Unity】ファイルサイズとメモリ消費を無意味に増やさない為の、AssetBundleと依存関係についてのメモ の内容ですが、追うのが正直面倒くさいので簡単に追う機能を試作しました。

AssetBundleが参照するアセットの依存関係(おさらい)

AssetBundleは、対象のアセットを構築する上で必要なアセットを、自動的に含めます。例えば、独自シェーダーとテクスチャを参照するEnemyマテリアルを参照する、EnemyというPrefabがあったとします。

このEnemyプレハブをAssetBundleに格納しロードした場合、特に参照してるテクスチャやシェーダー・マテリアルの存在を考えなくても、AssetBundleからプレハブをインスタンス化する事が出来ます。それは、Enemyプレハブが必要とする全てのアセットがAssetBundleに格納されるからです。

f:id:tsubaki_t1:20160611220611j:plain

この機能は有り難いのですが、知らないと少し厄介です。

例えば、Enemyと同様にPlayerというプレハブがあり、参照するマテリアルがシェーダーとテクスチャを参照していた場合、Enemyプレハブと同様にシェーダーとテクスチャをAssetBundleに含めます。

f:id:tsubaki_t1:20160611231448j:plain

 

つまり、Playerを格納したAssetBundleとEnemyを格納したAssetBundleは、同一のアセットを各々が格納してしまう訳です。

また、厄介な事は「異なるAssetBundleから呼び出したアセットは、(元が同一のアセットであっても)異なるアセットとしてロードされる」という点です。

つまり、この場合PlayerとEnemyのAssetBundleを読み込んだ場合、通常のおよそ倍のメモリとロード時間が必要になる訳です。

重複するアセットを見つける

この「重複しているアセット」は、「AssetBundle一つにつきPrefabを一つ格納」の様な作り方をしていると、気づかずに結構作られていたりします。シェーダー等は特に。

しかし残念ながら、Manifestに書かれているAssetBundleに含めるアセット一覧には、AssetBundle名が設定されているアセットしか書かれません。アセットが参照するアセットが参照するアセットが参照するアセット(ただし別のAssetBundleに格納されている場合は除く)を書いてはくれないのです。

f:id:tsubaki_t1:20160611235057j:plain

ということで、簡単に重複しているアセットを見つけるエディタ拡張を作りました。

 

Assetbundleの依存関係から重複アセットを見つけ出すプロトタイプ · GitHub

 

これを導入してAssetBundle/Namedを選択すると、重複しているアセットと、指定のアセットを使用しているAssetBundle名を見つける事が出来ます。

AssetBundle Nameではなく他のアプローチでAssetBundleを作成している場合は、この方法は効きません。

f:id:tsubaki_t1:20160611233035j:plain

f:id:tsubaki_t1:20160611232906j:plain

 

一応、プロジェクトに含まれるアセットから依存関係群を抽出しているため、AssetBundleをビルドしなくても確認できるようになっています。また、フォルダのAssetBundle指定やらにも一応対応はしています。
ただ糞コードなので過度な期待は勘弁してくだしあ。

重複したアセットを見つけたら

重複したアセットを見つけた場合、1~2個でかつメモリ的にもそこまで問題にならないのであれば、無視しても問題ないと思います。ただ、50~450くらいの量があった場合、酷い事になる事もあります。数kbのシェーダーがメガ行ってたりします。

この問題を解決する手っ取り早い方法は、重複しているアセットを別途AssetBundleにしてしまう事です。

例えばShaderはshader.abのようなアセットバンドルに格納し、プレハブから参照する際にロードする事により、ファイルサイズ・メモリサイズ・ロード時間的に効率的に読めます。

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

但し、同一インスタンスとして読めるのはAssetBimdleを「Unloadしていない場合」のみです。つまり、「AssetBundle一つにつきPrefabを一つ格納」でよくある「必要な時にロードし、不要になったら即Unload」のルールにて依存先のAssetBundleも即Unloadしている場合、依存関係でアプリサイズは減らせてもメモリとロード時間は負荷になるかもしれません。

 

最近の自分的にはAssetBundleはLoad/UnloadというよりはConnect/Disconnectの方がイメージに近いです。もしくはfopen/fclose。

wwwやmemoryからAssetBundleを取得した場合(5.3以前は)凄いメモリを喰っていたので開放するのは正しい事ではあるのですが、一応注意は必要です。
(5.3以降は、lzmaをmemoryから読んだ場合も無圧縮で展開するのではなくchunkを圧縮しながら展開するので、以前と比較してメモリ消費は安くなってると思います。確か)

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp