AssetBundle 加载、使用以及卸载
1.本地异步
1 IEnumerator LoadFromMemoryAsync(string path) 2 3 { 4 5 AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); 6 7 yield return createRequest; 8 9 AssetBundle bundle = createRequest.assetBundle; 10 11 var prefab = bundle.LoadAsset<GameObject>("MyObject"); 12 Instantiate(prefab); 13 14 }
2.本地同步(同步会造成主线程的卡顿,造成游戏画面的不流畅)
1 public class LoadFromFileExample extends MonoBehaviour { 2 function Start() { 3 var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle")); 4 if (myLoadedAssetBundle == null) { 5 Debug.Log("Failed to load AssetBundle!"); 6 return; 7 } 8 var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("MyObject"); 9 Instantiate(prefab); 10 } 11 }
3.从web端下载并加载 (要引用UnityEngine.Networking命名空间)
IEnumerator IE_LoadAssetBundleByName(string bundleName) { while (!Caching.ready) { yield return null; } string fileType; #if UNITY_STANDALONE_WIN || UNITY_EDITOR fileType = "file://"; #elif UNITY_ANDROID && !UNITY_EDITOR fileType = "jar://"; #endif UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(fileType + assetBundlePath + "/" , 0); yield return request.SendWebRequest();//开始请求 while (!request.isDone) { Debug.Log("正在下载:" + request.downloadProgress.ToString()); yield return 1; } if (request.isDone) { Debug.Log("完成"); } if (request.isNetworkError || request.isHttpError) { Debug.Log("错误"); } else//完成 { Debug.Log("完成可用"); AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //完成以后就能用了 if (ab == null) { yield break; } } request.Dispose(); }
加载好的AssetBundle使用以下这段代码
1 T objectFromBundle = bundleObject.LoadAsset<T>(assetName);
T是要加载的资源类型。
决定如何加载资源时有几个选项。我们有LoadAsset
,LoadAllAssets
和他们同行的异步LoadAssetAsync
和LoadAllAssetsAsync
分别。
这是如何同步从AssetBundles加载资源:
要加载单个GameObject:
1 GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);
要加载所有资源:
1 Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();
异步加载资源,在访问资产之前,您需要等待此操作完成。
单个资源:
AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName); yield return request; var loadedAsset = request.asset;
所有资源:
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync(); yield return request; var loadedAssets = request.allAssets;
但是,为了节省空间,我们在打包AssetBundle包的时候经常把多个资源的依赖项单独打包。比如说有两个模型modelA和modelB,他们共同使用了matA的材质球,这个matA就是modelA和modelB的依赖项,如果我们只加载modelA或者modelB,那么这两个模型的材质是丢失的。所以为了避免这个情况,我们要根据依赖文件来先找到依赖项并加载它(bundle包)。
加载依赖文件:
1 private void GetAssetBundleManifest(string path) 2 { 3 AssetBundle maniFestAb = AssetBundle.LoadFromFile(path + "/AssetBundles"); 4 if (maniFestAb == null) 5 { 6 Debug.Log("加载失败"); 7 return; 8 } 9 AssetBundleManifest manifest = maniFestAb.LoadAsset<AssetBundleManifest>("AssetBundleManifest");//AssetBundleManifest基本固定的,不用改 10 11 string[] strArrAllAb = manifest.GetAllAssetBundles();//获取所有包的包名(就是我们打包时候取的名字,连带了路径) 12 13 for (int i = 0; i < strArrAllAb.Length; i++) 14 { 15 //做你想做的事,推荐放到字典里,以后想加载的时候查找一次(废话) 16 } 17 AssetBundle.UnloadAllAssetBundles(true);//如果用不到了记得卸载资源,把我们要的信息放个一个字典里就好,只加载一次(再次废话)。 18 }
好了,我们虽然加载了资源,但是assetbundle包其实还留在内存中。这时候,为了节约性能,我们要把不用的资源删除。
可以使用这个: AssetBundle.Unload(bool)
参数区别:
a. 参数是true的时候是完全卸载资源,包括了AB包(AssetBundle,一下都简称AB包,每次都打太麻烦了)和实例化的资源。就算是正在引用的资源也会被卸载,如果使用不当,会造成纹理丢失之类的情况。应该确保资源确实没有哪里引用了,才调用AssetBundle.Unload(true);
b. 参数是false的时候会卸载所有没有被引用的资源,这就造成了源AB包已经被卸载,实例化出来、并被人使用者的某资源就没有了标记,再次加载这个物体的时候要重新加载ab包,加载你要的某资源,之前加载的某资源虽然没有用了,但是还存在于游戏内存中,这样下去就会越积越多,就炸了。
所以要调用一下 Resources.UnloadUnusedAssets(); 这个方法。另外,在游戏场景切换的时候,引擎也会自动的调用这个方法,把不存在引用的资源卸载。