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

この記事は、2019年5月時点の技術内容です。
Unityのバージョンは、「Unity5.6.6」で検証しているため、技術的には古い内容になります。

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

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

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

 

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

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

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

 

\IT未経験者からのサポートあり!転職サービス3選!!/

サービス名
未経験 未経験OK 未経験の転職専用 経験者向け
公開の求人数 ITエンジニア全体で1万件以上
ITエンジニア未経験で600件以上
未公開 5,000件以上
利用対象 全職種 IT特化 IT特化
特徴 ✓誰もが知る転職サービス
✓経歴を登録しておくとオファーが来る
✓企業担当者と条件交渉
✓スキルの身につく企業を紹介
✓IT専門のエージェントが対応
✓転職成功すると年収200万円以上の大幅アップがある
転職サポート内容
  • 求人検索
  • 企業担当者と交渉
  • 求人紹介
  • ライフプランのサポート
  • キャリア相談
  • 求人紹介
  • 提出書類の添削
  • 面接対策
公式サイト リクナビネクスト テックゲート レバテックキャリア

 

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;


		}
	}

 

検証のまとめ

検証の結果、以下のようにするとメモリ効率が良いことがわかりました。

  • 複数のプレファブで共通のテクスチャを使うときは、テクスチャを別のアセットバンドルに作成しておく
  • テクスチャを呼び出す前にテクスチャが入っているアセットバンドルを読み込んでおく

\IT未経験者からのサポートあり!転職サービス3選!!/

サービス名
未経験 未経験OK 未経験の転職専用 経験者向け
公開の求人数 ITエンジニア全体で1万件以上
ITエンジニア未経験で600件以上
未公開 5,000件以上
利用対象 全職種 IT特化 IT特化
特徴 ✓誰もが知る転職サービス
✓経歴を登録しておくとオファーが来る
✓企業担当者と条件交渉
✓スキルの身につく企業を紹介
✓IT専門のエージェントが対応
✓転職成功すると年収200万円以上の大幅アップがある
転職サポート内容
  • 求人検索
  • 企業担当者と交渉
  • 求人紹介
  • ライフプランのサポート
  • キャリア相談
  • 求人紹介
  • 提出書類の添削
  • 面接対策
公式サイト リクナビネクスト テックゲート レバテックキャリア

 

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

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

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

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

 

メモリを効率的に活用する方法まとめ

メモリを効率的いに使う方法まとめ

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

 

\IT未経験者からのサポートあり!転職サービス3選!!/

サービス名
未経験 未経験OK 未経験の転職専用 経験者向け
公開の求人数 ITエンジニア全体で1万件以上
ITエンジニア未経験で600件以上
未公開 5,000件以上
利用対象 全職種 IT特化 IT特化
特徴 ✓誰もが知る転職サービス
✓経歴を登録しておくとオファーが来る
✓企業担当者と条件交渉
✓スキルの身につく企業を紹介
✓IT専門のエージェントが対応
✓転職成功すると年収200万円以上の大幅アップがある
転職サポート内容
  • 求人検索
  • 企業担当者と交渉
  • 求人紹介
  • ライフプランのサポート
  • キャリア相談
  • 求人紹介
  • 提出書類の添削
  • 面接対策
公式サイト リクナビネクスト テックゲート レバテックキャリア
最新情報をチェックしよう!