AssetBundle設計コトハジメ
Unityの開発で動的にリソース(Asset)の更新を行う場合、AssetBundleを利用するのが主流だと思います。
今回は比較的規模が大きく、アプリケーションのアップデートをせずにリソース(Asset)を更新する仕組みを実装する際に、考慮すべきことを書いていきます。
前提としてUnity5.3以降のバージョンを対象とする内容になります。
自身で検証できていないものもあるので、世の中に溢れている情報から考慮すべき事項をご紹介、ということでご容赦いただければと思います。
実例を知りたい方は次の記事(AssetBundle設計のとある形)を読んでいただければと思います。
①1つのAssetBundleに含めるAssetの数やルール
例えば、ゲームで利用するすべてのAssetを1つのAssetBundleに含めてしまい、ゲームの起動時にそのAssetBundleをダウンロードするようにします。
そうすると、Assetを利用する箇所では、ダウンロードが終わっている前提で、Assetの有無を気にせず実装することができ、非常にシンプルになります。
しかし、規模が大きくなってくると、AssetBundleのサイズが大きくなってダウンロード時間が長くなり、切断時のリスクなども増えてきます。
そこでAssetBundleの分割を検討する場合、まずは単純に適度なサイズに分割し、ダウンロードの進捗を表示する、というのがよくあるパターンです。
圧縮形式によっては、1つのAssetをロードするだけで、AssetBundle全体の解凍が必要になります。その場合は無駄なメモリを消費することになるので、AssetBundleに含めるAssetはそれを考慮して選んだ方がよいでしょう。圧縮形式については後述します。
AssetBundleのダウンロードを起動時に集約すると、ダウンロード時間が長くゲームがなかなか始まらない、という不都合もでてきます。そこでもう少し動的に作りたい場合、画面(シーン)単位に必要なAssetをまとめたり、カードゲームであればキャラクターやカード毎にAssetをまとめたり、利用する直前でダウンロードしてからAssetをロードするなどの工夫が必要になってきます。
実際のゲーム画面は上図のようなイメージになります。
また、1Asset = 1AssetBundleのような形にしてしまって、オンライン環境であればプレイする人にリソースダウンロードが走っている感覚をあたえないようにする、というのもUnityでは比較的やりやすい設計です。
AssetBundleにつけられる名前は小文字になるので、1Asset = 1AssetBundleで同じファイル名にしたい場合はその点も考慮しましょう。
「AssetBundleに含めるAssetの数やルール」として考慮すべきことをまとめると、
こんなところでしょうか
・ダウンロードするタイミング
・サイズ + 同時利用される単位 + 依存関係(後述)
②Unloadするタイミング
AssetBundleに含まれているAssetをロードする場合、当然AssetBundleそのものもロードする必要があるので、同時利用しないAssetの分もメモリに負荷がかかります。そのため、AssetBundleから必要なAssetのロードが終わったら、基本的にはAssetBundle.UnloadでAssetBundleを解放しておくのが良いでしょう。
Unity5.3から登場したLZ4の圧縮形式であれば、分割圧縮されているのでロード時に必要な分だけメモリが使用されるようになっています。
また、既にロード済みのAssetBundleをロードしようとすると、Unityはエラーを返してくるので、その辺りも考慮した実装が必要になります。
依存関係などの都合で、頻繁に利用されるAssetBundleはUnloadしないというのも一つの手です。
③圧縮形式
Unityがサポートしている圧縮形式は、それぞれ以下の特徴があります。
非圧縮 | サイズが大きい、ロード速い |
LZMA | サイズが小さい、ロード遅い |
LZ4 | バランス |
ざっくりこんな感じです。
Unity5.3から登場したLZ4が圧縮率もよくロードも速いので人気なようです。
LZ4形式にしたい場合は、AssetBundleビルド時にBuildAssetBundleOptions.ChunkBasedCompressionを指定します。
AssetBundleの数が膨大だと、Caching.readyがtureになるまでの時間が遅くなるなどで不人気なキャッシュ(WWW.LoadFromCacheOrDownload、UnityWebRequest.GetAssetBundle)ですが、これを利用すれば
LZMA(ダウンロード時) → LZ4(端末に保存、ロード時)
といったことも可能で、デフォルト設定でこの形になっています。(Caching.compressionEnabled)
④ロード方法
AssetBundle.LoadFromMemory(Async) | メモリ上のデータ(byte[])からロードする。 そのため一度メモリに展開する必要がある。 AssetBundleを暗号化する場合は、復号化した データをこの方法でロードする形になりそう |
AssetBundle.LoadFromFile(Async) | ダウンロード&ローカルストレージに保存し、 そのファイルパスからロードする。 LZMA形式の場合、一度メモリに展開されるのでLoadFromMemoryと同等のメモリ負荷がかかる |
キャッシュ (WWW.LoadFromCacheOrDownload、UnityWebRequest.GetAssetBundle) |
ダウンロードするURLからロード&キャッシュ する。 数が膨大だとCaching.readyがtureになるまでの時間が長くなる。 更新や削除が不便。 →versionを上げても、古いAssetBundleが キャッシュに残る →同一versionでcrcを更新すれば上書き可能らしい →AssetBundle個別の削除ができない →crcにダミー値を入れて削除、というあまり 推奨できない消し方はある |
⑤AssetBundleビルドの工夫
規模の大きいサービスだとリソースの量も多くなってくると思います。AssetBundleをビルドするプロセスは、量が少なければUnityが用意してくれている機能だけで事足りるかもしれませんが、多くなれば効率化を考えていくべきでしょう。
AssetPostprocessor(AssetImporter)でAssetBundle名を設定したり、ビルド時にAssetBundleManifestが生成されるのでチェックしたりと、色々と自動化の工夫は考えたほうがよいです。AssetGraphとかを試すのもよいかもしれません。
また、アプリ開発用とAssetBundle管理用でgitのリポジトリを分けたり、AssetBundleビルド専用のマシンを用意したり、Unity Cloud BuildでAssetBundleビルドをしたり、など、どのように管理するのが最適化を考えましょう。
⑥依存関係とAssetBundleManifest
AssetBundleからロードするAsset①が他のAssetBundleに含まれているAsset②への参照をもっている場合、その依存関係を解決する必要があります。具体的にはAsset①をロードする前に、依存関係のあるAsset②が含まれるAssetBundleを事前にロードしておく必要があります。
依存関係は、AssetBundleのビルドを行う際に生成されるマニュフェストファイル(AssetBundleManifest)から確認できるので、上記を考慮した設計を考えましょう。
この依存関係をアプリ内で動的に解決するような仕組みをつくるのであれば、公式が提供しているAssetBundleManagerなどを参考にすると良いと思います。AssetBundleをロードする際に、依存関係のあるAssetBundleを勝手にロードしてくれる、という仕組みになっています。
⑦まとめ
Unity5.3からLZ4がサポートされ、より利用しやすくなったAssetBundleですが「これが正解!」と答えがあるものではないので、最適な環境をつくるにはプロジェクトでいろいろな工夫が必要です。ここで記載したこと以外にも考慮するべきところはでてくるかもしれませんが、参考になれば幸いです。
次の記事で、今携わっている開発で実際にどのような設計になっているかをご紹介しているので、興味がある方はそちらも読んでみてください。
参考
●Unityマニュアル
https://docs.unity3d.com/jp/current/Manual/AssetBundlesIntro.html
●Unityスクリプトリファレンス
https://docs.unity3d.com/jp/current/ScriptReference/AssetBundle.html
●Unite 2016 TOKYO「学校では教えてくれないアセットバンド ルのしくみ」
http://japan.unity3d.com/unite/unite2016/files/DAY1_1800_Room1_Ebata.pdf
●Unity道場第十回目「アセット運用ベストプラクティス」
http://sssslide.com/speakerdeck.com/unitydojo/asetutoyun-yong-besutopurakuteisu
この記事へのコメントはありません。