Unityにより,アプリケーションにおいて,特定のアセットバンドルには一度にひとつのインスタンスのみ持つことができます。つまり,もし同じものが以前にロードされていてアンロードされていなければ,WWWオブジェクトからアセットバンドルを取得できないということです。実際的には以前にロードされたアセットバンドルをアクセスするときは次のようにします:
AssetBundle bundle = www.assetBundle;
次のエラーが返されます
Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle
そして assetBundle は null を返します。もし一回目がまだロードされている場合,二回目のダウンロードではアセットバンドルを取得できないため,やらなければいけないこととしては,アセットバンドルを使用しなくなったときに アンロード するか,または参照を維持して,すでにメモリにあるときはダウンロードすることを避けることです。ニーズによって,取るべきアクションは判断できますが,推奨はオブジェクトのロード完了したら直ちにアセットバンドルを_アンロード_ することです。これによりメモリが解放され,キャッシュしたアセットバンドルをロードするときのエラーが出なくなります。
Please note that before Unity 5 all bundles would finish loading before any of the bundles would be unloaded. So calling AssetBundle.Unload function while some of the bundles are still being loaded will block the execution of the rest of the code until all bundles are loaded. This will add a performance hiccup. This has been reworked in Unity 5.
もしどのアセットバンドルがダウンロードされたかトラッキングする場合は,ラッパークラスを使ってダウンロードを次のように管理します:
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
static public class AssetBundleManager {
// A dictionary to hold the AssetBundle references
static private Dictionary<string, AssetBundleRef> dictAssetBundleRefs;
static AssetBundleManager (){
dictAssetBundleRefs = new Dictionary<string, AssetBundleRef>();
}
// Class with the AssetBundle reference, url and version
private class AssetBundleRef {
public AssetBundle assetBundle = null;
public int version;
public string url;
public AssetBundleRef(string strUrlIn, int intVersionIn) {
url = strUrlIn;
version = intVersionIn;
}
};
// Get an AssetBundle
public static AssetBundle getAssetBundle (string url, int version){
string keyName = url + version.ToString();
AssetBundleRef abRef;
if (dictAssetBundleRefs.TryGetValue(keyName, out abRef))
return abRef.assetBundle;
else
return null;
}
// Download an AssetBundle
public static IEnumerator downloadAssetBundle (string url, int version){
string keyName = url + version.ToString();
if (dictAssetBundleRefs.ContainsKey(keyName))
yield return null;
else {
using(WWW www = WWW.LoadFromCacheOrDownload (url, version)){
yield return www;
if (www.error != null)
throw new Exception("WWW download:" + www.error);
AssetBundleRef abRef = new AssetBundleRef (url, version);
abRef.assetBundle = www.assetBundle;
dictAssetBundleRefs.Add (keyName, abRef);
}
}
}
// Unload an AssetBundle
public static void Unload (string url, int version, bool allObjects){
string keyName = url + version.ToString();
AssetBundleRef abRef;
if (dictAssetBundleRefs.TryGetValue(keyName, out abRef)){
abRef.assetBundle.Unload (allObjects);
abRef.assetBundle = null;
dictAssetBundleRefs.Remove(keyName);
}
}
}
このクラスの使用例は:
using UnityEditor;
class ManagedAssetBundleExample : MonoBehaviour {
public string url;
public int version;
AssetBundle bundle;
void OnGUI (){
if (GUILayout.Label ("Download bundle"){
bundle = AssetBundleManager.getAssetBundle (url, version);
if(!bundle)
StartCoroutine (DownloadAB());
}
}
IEnumerator DownloadAB (){
yield return StartCoroutine(AssetBundleManager.downloadAssetBundle (url, version));
bundle = AssetBundleManager.getAssetBundle (url, version);
}
void OnDisable (){
AssetBundleManager.Unload (url, version);
}
}
このサンプルでAssetBundleManager クラスがstaticであることに留意する必要があり,新しいシーンをロードするとき参照する全てのアセットバンドルは破棄されません。このクラスをガイドとして使用するのは良いですが,最初に述べたように使用した直後にアセットバンドルを アンロード するのがベストです。先にインスタンス化されたオブジェクトをいつでもクローンすることが可能で,再度アセットバンドルをロードする必要がなくなります。