Unity開発・余分なメモリを使わないAssetBundleの作り方レシピ

 

UnityのAssetBundleを作るときの、どのように作成すれば使用メモリーを削減できるのか、いろいろ試してみた。

 

その結果。buildMapを用いて、テクスチャ画像とプレファブ(画像以外)を別々のAssetBundleにしたほうが良いという結論に至りました。

 

ただし、1枚のテクスチャを一つのプレファブのみで使う場合は、画像とプレファブを分けなくてもよいです。

 

 

検証結果をサンプルソースも交えて解説します。

 

サンプルソースの全体は、GitHubにあるので必要があれば参考にしてください!

https://github.com/jshirius/unity-AssetBundleSample

 

 

AssetBundleの検証

 

■開発環境

Unity5.6.6

 

 

リソースに関する説明

 

■テクスチャ(画像)

以下の2枚の画像(テクスチャ)用意する

・image1(3Mほど)

・image2(3Mほど)

 

※画像について

画像は、iPhoneアプリの「カスタムキャスト」の画像を使わせていただいております。

 

■プレファブ

以下の3つのパターンのプレファブを用意します。

 

PrefabA

・image1テクスチャを使用

 

PrefabB

・image2テクスチャを使用

 

PrefabAB

・image1テクスチャを使用

・image2テクスチャを使用

※つまり2枚のテクスチャを使います。

 

 

 

検証開始

 

AssetBundleは、依存関係(例えば、プレファブにテクスチャが割り振られていたらテクスチャも含めてくれる)を自動的に解決してくれます。

 

しかし、アセットバンドルの作り方、呼び出し方によっては、無駄にメモリをつかってしまうので注意が必要です。

 

そこで、3パターンほど検証してみます。

なお、buildMapは1つとします。

 

 

(1)PrefabAのみを指定してアセットバンドル「prefab_bundle_prefabA_only」を作る

 

 

PrefabAのみを指定してアセットバンドル「prefab_bundle_prefabA_only」を作ります。

その結果、prefab_bundle_prefabA_onlyには、

・PrefabA

・PrefabAの依存関係にあるimage1

が梱包されます。

 

■メモリに展開されるものは?

prefab_bundle_prefabA_onlyを展開してPrefabAを読み込んだときは、

メモリーに以下のデータが展開されます。

・PrefabA

・PrefabAの依存関係にあるimage1

 

このときは、メモリの無駄はないと言えます。

※メモリへの展開は、Unityのプロファイラーを使って確認しています。

 

 

(2)PrefabABのみを指定してアセットバンドル「prefab_bundle_prefabAB_only」を作ります

 

PrefabABのみを指定してアセットバンドル「prefab_bundle_prefabAB_only」を作ります。

その結果、prefab_bundle_prefabA_onlyには、

・PrefabAB

・PrefabABの依存関係にあるimage1

・PrefabABの依存関係にあるimage2

が梱包されます。

 

 

■メモリに展開されるものは?

prefab_bundle_prefabAB_onlyを展開してPrefabABを読み込んだときは、

メモリーに以下のデータが展開されます。

・PrefabA

・image1

・image2

 

このときは、”(1)のアセットバンドル「prefab_bundle_prefabA_only」を読み込んでいなければ”、メモリの無駄はないと言えます。詳しくは、次の項目で解説します。

 

 

アセットバンドル「prefab_bundle_prefabA_only」と「prefab_bundle_prefabAB_only」をよみこんだら

 

 

さて、アセットバンドル「prefab_bundle_prefabA_only」「prefab_bundle_prefabAB_only」の2つを読み込んだらどうなるでしょう?

 

答えは、以下のデータがメモリに読み込まれます。

・PrefabAB

・PrefabABの依存関係にあるimage1

・PrefabABの依存関係にあるimage2

・PrefabA

・PrefabAの依存関係にあるimage1

 

 

あれ?

image1が重複してますね。

つまり、「prefab_bundle_prefabA_only」「prefab_bundle_prefabAB_only」は別々のアセットバンドルなので、image1が2回呼び出されて無駄にメモリに格納されてしまうのです。

 

このように、アセットバンドルの作り方によっては、無駄にメモリを使ってしまうのです。

 

 

(3)テクスチャとプレファブそれぞれ別のアセットバンドルを作成する

 

■imageアセットバンドル「image_bundle」

梱包するものは、以下の通り。

・image1

・image2

 

■プレファブ用のアセットバンドル「prefab_bundle」

梱包するものは、以下の通り。

・PrefabA

・PrefabAB

 

 

buildMapで「image_bundle」を「prefab_bundle」を作成します。

ポイントは、テクスチャとプレファブを分けています。

 

構成は以下の通り

 

アセットバンドル作成部分のソースコードは以下の通り。


//AssetBundleをプレファブとテクスチャを分けたとき、アセットバンドルも分けて作成されるか確認する
	[MenuItem("Example/Build prefab_and_image")]
	static void BuildMapImageAndPrefabABs()
	{
		// Create the array of bundle build details.
		AssetBundleBuild[] buildMap = new AssetBundleBuild[2];

		buildMap[0].assetBundleName = "image_bundle";

		//テクスチャ用のアセットバンドル
		string[]  imageAssets = new string[2];
		imageAssets[0] = "Assets/AssetBundle/ImageAssets/image1.png";
		imageAssets[1] = "Assets/AssetBundle/ImageAssets/image2.png";

		buildMap[0].assetNames = imageAssets;


		//プレファブ用のアセットバンドル		
		buildMap[1].assetBundleName = "prefab_bundle";
		string[] prefabAssets = new string[4];
		prefabAssets[0] = "Assets/AssetBundle/PrefabAssets/PrefabA.prefab";
		prefabAssets[1] = "Assets/AssetBundle/PrefabAssets/PrefabB.prefab";
		prefabAssets[2] = "Assets/AssetBundle/PrefabAssets/PrefabAB.prefab";
		prefabAssets[3] = "Assets/AssetBundle/PrefabAssets/abc.txt";

		buildMap[1].assetNames = prefabAssets;

		BuildPipeline.BuildAssetBundles("Assets/ABs", buildMap, BuildAssetBundleOptions.None, BuildTarget.iOS);
	}

 

■アセットバンドルの読み込み

 

アセットバンドルの読み込むときは、まず「image_bundle」から読み込みます。

次に「prefab_bundle」を読み込みます。「image_bundle」を読み込んでおかないと、「prefab_bundle」内のプレファブをゲームオブジェトとしてインスタンス化したときに

テクスチャが反映されないので注意が必要です。

 

PrefabA,PrefabABを読み込んだときメモリには以下の情報が読み込まれます。

 

答えは、以下のデータがメモリに読み込まれます。

・PrefabAB

・PrefabA、PrefabABの依存関係にあるimage1

・PrefabABの依存関係にあるimage2

 

image1が一回だけ読み込まれていることがわかります。

 

 

つまり、prefab_bundleA,prefab_bundleABを読み込んでも

image1,image2の1つずつのテクスチャ読み込みで終わったことを確認取れた。

 

アセットバンドル読み込み部分のソースコード


	IEnumerator LoadCacheOrDownload(){
		Debug.Log("LoadFromCacheOrDownload begin");

		//さきにimageを読み込んでおく必要がある。でないと、プレファブを表示したときにテクスチャがない状態になる
		using (var www = WWW.LoadFromCacheOrDownload("http://localhost/assetbundles/image_bundle", 5))
		{
			yield return www;
			//Debug.Log(String.format "LoadFromCacheOrDownload {0}" , www.assetBundle.ToString());


			imageAsset = www.assetBundle;
			//var asset = myLoadedAssetBundle.mainAsset;

		}

		//プレファブを読み込む
		using (var www = WWW.LoadFromCacheOrDownload("http://localhost/assetbundles/prefab_bundle", 5))
		{

			Debug.Log("LoadFromCacheOrDownload begin2");
			yield return www;
			Debug.Log("LoadFromCacheOrDownload begin3");
			if (!string.IsNullOrEmpty(www.error))
			{
				Debug.Log(www.error);
				yield return null;
			}

			//Debug.Log(String.format "LoadFromCacheOrDownload {0}" , www.assetBundle.ToString());


			var myLoadedAssetBundle = www.assetBundle;

			Debug.Log("LoadFromCacheOrDownload OK");

			foreach (string s in myLoadedAssetBundle.GetAllAssetNames()) {
				Debug.Log(s);
			}


			//アセットバンドルデータ
			prefabAsset = myLoadedAssetBundle;


		}
	}

 

検証のまとめ

複数のプレファブで共通のテクスチャを使うときは、テクスチャを別のアセットバンドルに作成しておき、テクスチャを呼び出す前にテクスチャが入っているアセットバンドルを読み込んだほうがメモリ効率が良いことがわかりました

 

 

 

使ったアセットバンドルは解放するとメモリに優しい

使ったアセットバンドルは、必要がなくなったら解放します。

 

特にテクスチャは、どのゲームオブジェクトからの参照が切れていても解放されません(体験談)。使わなくなったら「AssetBundle.Unload (true)」を呼び出して解放してあげます。

引数のtrueは、強制的に解放するものですが、私が知る限りでは確実に解放することができます。

 

 

まとめ

 

まとめると以下の通りになります。

 

 

まとめ

  1. アセットバンドルはテクスチャ(複数のゲームオブジェクトから参照される)とプレファブは別々に作成する
  2. ただし、テクスチャとプレファブが1対1の関係なら分けてアセットバンドルを作成する必要はない
  3. 使わなくなったアセットバンドルは、「AssetBundle.Unload (true)」を呼び出して即メモリから解放してメモリ容量の削減する

 

最新情報をチェックしよう!
>プログラミングスクール検索・比較表サイト

プログラミングスクール検索・比較表サイト

ワンクリック、さらに詳細に条件を指定してプログラミングスクールの検索ができます。さらに比較表により特徴を細かく比較できる!

CTR IMG