アプリケーションにおいて、特定のアセットバンドルには一度にひとつのインスタンスのみ持つことができます。つまり、もし同じものが以前に読み込まれていて破棄されていなければ、WWW オブジェクトからアセットバンドルを取得できないということです。実際には、以前読み込まれたアセットバンドルにアクセスするときは次のようにします。
AssetBundle bundle = www.assetBundle;
次のエラーが返されます。
Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle
そして assetBundle は null を返します。もし一回目がまだ読み込まれている場合、二回目のダウンロードではアセットバンドルを取得できないため、やらなければいけないことは、使用しなくなったときに 破棄 するか、参照を維持して、すでにメモリにあるときはダウンロードすることを避けることです。ニーズによって、取るべきアクションは自由ですが、推奨はオブジェクトの読み込みが完了したら直ちにアセットバンドルを 破棄 することです。これによりメモリが解放され、キャッシュしたアセットバンドルを読み込むときのエラーが出なくなります。
Unity 5 以前は、アセットバンドルを破棄する前に、すべてのアセットバンドルの読み込みが完了していなければならないことに注意してください。いくつかのアセットバンドルがまだ読み込まれている間、AssetBundle.Unload 関数を呼び出してもすべてのアセットバンドルが読み込まれるまでコードを実行しません。これはパフォーマンスの遅延やしゃっくり状態(hiccup)を発生させます。また、これは 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 {
while (!Caching.ready)
yield return null;
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 であることに注意する必要があり、新しいシーンを読み込むときに参照するすべてのアセットバンドルは破棄されません。このクラスをガイドとして使用するのはよいですが、最初に述べたように使用した直後にアセットバンドルを 破棄 するのがベストです。先にインスタンス化されたオブジェクトをいつでも複製することが可能で、再度アセットバンドルを読み込む必要がなくなります。