今回はSpritePackerとAssetBundleの挙動について少しメモします。
SpritePackerとAssetBundleといふ便利機能
SpritePackerは2Dゲームでよくある手法で、スプライトを一つにまとめる事でSetPassの発生を抑える機能です。この機能を使用すると、テクスチャにタグを設定することで、スプライトをテクスチャアトラスとしてまとめめる事が出来ます。
通常はスプライトを事前にパックしたテクスチャをスライスしてゲームに使用するのですが、SpritePackerはその辺りを自動的に設定してくれます。
テクスチャアトラスにする事で何が良いかと言えば、例えば同一のテクスチャにひとまとめにしたスプライトを並べた場合、SetPassは1で済みますが、異なるテクスチャからスプライトを拾う場合、SetPassが2になります。
AssetBundleはUnityの代名詞とも言える機能で、テクスチャや3DモデルやScriptableObjectといったアセットを外部ファイルから読みだす機能です。
これらのアセットは通常であればゲームビルド時に組み込まなくてはいけないのですが、AssetBundleを使用する事でビルド後に加算・変更を加える事が可能になります。
どちらも機能としては分かりやすい物ですが、この二つを合わせるとどうなるのでしょう。確認してみました。
SpritePackerでパックしたテクスチャアトラスはAssetBundleに自動的に組み込まれる
SpritePackerでパックしたアトラスをAssetBundleに含める方法は非常に単純です。スプライトをそのままAssetBundleに含めればアトラスの方が含まれます。
例えば、キャラクターのスプライトアニメーションをパッキングし、スプライトアニメーションを行うPrefabをAssetBundleに含めた場合、パッキング前のスプライトではなくテクスチャアトラスが含まれます。
SpritePackerでパックしたテクスチャアトラスの部分組込は出来ない
ここで一つ期待があったのが、テクスチャアトラスの部分組込ですが、結論を言うと出来ません。
期待した動作はスプライトを使う際にAssetBundleが使用するスプライトのみを抜きだして取得する…といった物なのですが、残念ながらテクスチャアトラス化した場合、アトラスがまるまる含まれてしまいます。
もし単一のスプライトのみを使用したい場合は、一時的にResourcesフォルダ以下にでもスプライト群を放り込めばテクスチャアトラスは解除されるのですが(Resourcesの動作を考えれば分かる通り、SpritePackerはResourcesフォルダに対応していません)、ワークフローがややこしくなるので、テクスチャを削除してしまった方が無難かもしれません。
AssetBundleの依存関係を構築しない場合、2重にテクスチャが読まれる事がある
もう一つの問題が、AssetBundle毎にテクスチャを保持してしまった場合の問題です。
AssetBundleはビルドする際、必要と思われる全てのアセットを自身に保持するのですが、ここで注意すべき点は他のAssetBundleが含めたアセットもAssetBundleに含めてしまう点です。
例えば下の画像のように、同じテクスチャアトラスを参照するAssetBundleAとAssetBundleBを作ったとします。
この場合、AssetBundleAとAssetBundleBの双方にテクスチャアトラスが含まれてしまい、AssetBundleのファイルサイズ的に無駄になるどころか、消費メモリも倍になります。
これを解決する手っ取り早い方法は、AssetBundleの依存関係を作る事です。AssetBundleの依存関係を作り単一のAssetBundleからテクスチャアトラスを取得することで、メモリ消費量的にもロード時間的にもSetPass的にも無駄なく読み込めます。
このやり方はUnity 4では若干面倒でしたが、現在は非常に簡単になりました。テクスチャアトラス用に使用するテクスチャを別のAssetBundleとして作成し、事前に該当のAssetBundleをロードして置くだけです。
テクスチャアトラスを別スプライトとして明確に作っておくと、UnityはAssetBundleを構築する際、自動的に依存関係を構築してくれます。*1
確かにこの挙動を期待するならば、上の「SpritePackerでパックしたテクスチャアトラスの部分組込は出来ない」は理に適っているのかもしれません。
Spriteシェーダーを使用したマテリアルを用意する
SpriteのPrefabをAssetBundleに格納する場合、Spriteのマテリアルを別途用意して上書きしなければドローコールが増えます。
SpriteマテリアルをAssetBundleに格納するのを忘れずに。
関連
(現在AssetBundleには非対応だけど、まぁ使えなくは)
*1:Unity4では自前でPush/Popを設定する必要があった