今回はAssetBundleにSceneを格納し、ロードする手法について紹介します。
上手く使うと、ステージの読み込みやローカライズしたUIの表示等に使えそうです。
目次
AssetBundleの新しいサンプル、
AssetBundleManager
今回はAssetBundleManagerを使用します。AssetBundleManagerはAssetBundleを管理するAPIを使いやすくパックした物です。
AssetBundleManagerはここから入手出来ます。
AssetBundleManagerを構築は
フォルダかファイルにAssetBundleタグを指定
詳細な事は置いておいて、とりあえずの使い方はこんな感じです。まずはシーンをAssetBundle化します。
今回はこんなシーンをAssetBundle化します。シーンの中にUIを一つだけ置いたシンプルなシーンです。
シーン(.unity)もしくはシーンのフォルダを選択した状態で、InspectorのAssetBundleを選択します。
「New...」を選択し、名前を「test」と入力します。これがファイル名です。
命名はファイル名・フォルダ名とも関係するため、/(スラッシュ)をはじめとした「フォルダと関係する命名もしくはファイル名に使えない名前」は使わない方が良さそうです。
フォルダをAssetBundle化した場合、該当するフォルダ以下のファイルがAssetBundleに含まれます。
後はメニューバーのAssets > AssetBundles > Build AssetBundles を選択すると、AssetBundleを選択中のプラットフォーム向けでビルドしてくれます。
ちなみにSceneとアセットは同一タグでビルド出来ません。つまりタグを設定したフォルダにアセットが含まれてる場合はビルド失敗するので、その辺り注意です。
この機能は「AssetBundleのビルドが不要なAssetBundle(内部構造が更新されていないもの)」はビルドがスキップされます。
これで長かったビルド時間もある程度はなんとかなるねヤッター。
AssetBundleに保存されているシーンをロードする
今度は呼び出しです。サーバーを立てるのが面倒なので、シミュレータモードを使用します。
まずはメニューバーのAssets > AssetBundle > Loadl AssetBundle Serverのチェックを入れます。
あとは実際にAssetBundleに格納したシーンを呼び出すコードを追加します。
Simulator Modeを使用するので、以下のコードを使用してサーバーを初期化できるようにしておきます。
#if DEVELOPMENT_BUILD || UNITY_EDITO
AssetBundleManager.SetDevelopmentAssetBundleServer ();
#endif
あとはAssetBundleManagerを初期化して、AssetBundle名「test」の「sample」シーンを「LoadLevelAditive」ではないモードで呼び出します。
yield return StartCoroutine (AssetBundleManager.Initialize ());
yield return StartCoroutine( AssetBundleManager.LoadLevelAsync ("test", "sample", false));
これでAssetBundleをWebからダウンロードし、AssetBundleに格納したシーン(ステージやUI)をロードする事が出来ます。
AssetBundleからロードするシーンは、Scenes In Buildに入れてなくても呼び出せます。なので、ゲームのリリース後にステージを配信…みたいな事も可能です。ただしスクリプトはゲームビルド時に含まれていた物しか使えないので、その辺りは注意が必要そうです。
AssetBundleの依存関係とアセットの重複問題は
AssetBundleタグにある程度お任せ
AssetBundleはアセットが使用するリソースをAssetBundleに含めます。
ここで問題なのが、アセットの重複参照です。例えば二つのシーン「Scene_A(黄色)」と「Scene_B(緑)」があるカリスマ性に溢れる圧倒的にさすがって感じのモデルを参照していたとします。
SceneA
SceneB
このままシーンをビルドすると、Scene_AとScene_Bの双方に「圧倒的カリスマをry」のモデルが含まれてしまう事になります。
そこで、Scene_AとScene_Bが参照しているモデルをAssetBundle化してしまいます。
やり方は同じように、親フォルダを選択してInspectorでAssetBundleのタグを設定します。
これでビルドすると、Scene_AとScene_Bから「(略)カリスマモデル」が抜かれ独立したNuggetというAssetBundleが作成されます。
この状態でscene_aを呼んだらどうなるかと言うと、ちゃんとscene_aを呼んだ際にマニフェストの依存関係にあるnuggetをダウンロード・展開してくれるので、依存関係とか色々考えなくても普通に呼べます。
ついでに、マテリアルやテクスチャを少し更新してAssetBundleを更新すると、関連するAssetBundleだけ更新され、該当のマテリアルを呼んでいるシーンは変更済みマテリアルを呼び出せるみたいです。
AssetBundleのビルドが面倒なら
Simulator Modeを使う
AssetBundleの構築に時間がかかったり、そもそもAssetBundle構築が面倒くさいといったケースでは、Simulator Modeを使用します。
このモードはAssetBundleではなくローカルのリソースをAssetBundleっぽく呼び出します。つまり、AssetBundleを構築しなくても使えます。
但しAssetBundleのファイルパスを直で探しにいくため、VariantsおよびVariantsを使ったシーンは呼び出すことが出来ない点に注意が必要です。
使い方は、まずSimulator Modeのチェックを入れます。
次に、コードを下のように変更します。AssetBundleManager.Initializeはサーバー上のマニフェストに該当する奴を取り出すものなので、Simulator Modeでは不要です。
if (AssetBundleManager.SimulateAssetBundleInEditor == false) {
yield return StartCoroutine (AssetBundleManager.Initialize ());
}
これでロードすると、AssetBundleを構築していなくともシーンをロードする事が出来ます。正規のロードでは無いため(?)GIが付属していませんが、とりあえず動かして確認したい場合は有用です。
ロードするAssetBundleを切り替えたり、
シーンが参照するリソースを差し替えたり。
ローカライズやHD/FHDの切りかえに便利なのがVariantsです。簡単に言えば、該当のタグを持つAssetBundleをロードしてくれる機能です。
試しに日本語と英語のシーンを作成し、Variantsでロードしてみます。
英語と日本語、二つの同名のシーンを用意します。二つともAssetBundleタグは同一の「Scene」を設定しますが、第二のタグを「jp」と「en」で差別化します。
後は、以下のコードでどのVariantsを使用するかを設定します。このコードはAssetBundleをロードする前に設定します。
AssetBundleManager.ActiveVariants = new string[]{"jp"};
あとは今まで通りAssetBundleManager.LoadLevelAsync
でシーンをロードすると、jpと指定していたらjpのシーン、enと指定していたらenのシーンが呼ばれます。
フォントは各シーンが参照している物が使われます。もし言語毎にフォントが違う場合は、fontもVariantsで分別しておけば各言語毎にフォントが切り替えられます。
これの応用のリソースの差し替え機能は割と応用が効きそうです。アセット関連にしか使えないっぽいですが、
例えばゲーム内のUIとして旗を張り(実際は各言語毎のUI)それをVariants毎に切り替える操作を試してみます。
まず町田の旗を張っておきます。この二つは同じリソースを参照しています。
次にenのAssetBundleタグを持つフォルダに町田の旗と同名ファイルで厚木市の旗を配置します。
で、enタグでシーンをロードすると、厚木市の旗を表示します。
感想
もっと細かく挙動を調整したければAssetBundleManagerを解析するも良し、ちゃんとフローを眺めるもよし。
Manifestの概念のお陰で、スクリプト更新しても古いAssetBundleが使えるのは中々に良い感じです(以前はスクリプトを追加・削除すると使えなくなった)
色々と使えそうです。