Unity에서는 특정 에셋 번들의 인스턴스를 한 번에 하나씩만 애플리케이션에 로드할 수 있습니다. 즉, 이전에 로드된 후 언로드되지 않은 에셋 번들을 WWW 오브젝트에서 검색해서 가져올 수 없습니다. 실제 예를 들어서 설명하면, 다음과 같이 이전에 로드된 에셋 번들에 액세스하려고 할 경우
AssetBundle bundle = www.assetBundle;
다음 오류가 발생하고
Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle
에셋 번들 프로퍼티가 null을 반환합니다. 첫 번째 에셋 번들이 아직 로드되어 있는 경우 두 번째 다운로드 중에 에셋 번들을 검색해서 가져올 수 없으므로, 에셋 번들을 더 이상 사용하지 않을 때 언로드_하거나, 에셋 번들이 이미 메모리에 있는 경우 번들에 대한 레퍼런스를 유지하고 가급적 다운로드하지 않아야 합니다. 필요에 따라 적절한 방법을 결정할 수 있지만, 오브젝트가 전부 로드된 후 에셋 번들을 즉시 언로드_하는 것이 좋습니다. 그러면 메모리를 다시 사용할 수 있게 되고 캐싱된 에셋 번들을 로드하는 것과 관련된 오류가 더 이상 발생하지 않습니다.
Unity 5보다 오래된 버전에서는 모든 번들의 로딩이 완료된 후에만 번들이 언로드되었습니다. 따라서 일부 번들이 아직 로드 중일 때 AssetBundle.Unload 함수를 호출하면 모든 번들이 로드될 때까지 나머지 코드의 실행이 차단되었습니다. 이로 인해 성능 문제가 가중되었습니다. 이 문제는 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 클래스는 정적이고, 사용자가 레퍼런스하는 에셋 번들은 새 씬을 로드할 때 소멸되지 않습니다. 클래스를 참고하되, 권장 방법에 따라 처음에는 에셋 번들을 사용한 후 곧바로 _언로드_하는 것이 가장 효과적입니다. 이전에 인스턴스화한 오브젝트를 언제든지 복제하여 에셋 번들을 다시 로드해야 하는 필요성을 없앨 수 있습니다.