Unity 5 Asset Bundles 基本介绍(二)
下载 AssetBundles
有两种方法下载:
- 非缓存:通过构造一个WWW对象完成。AssetBundles不会缓存到本地存储设备上的Unity缓存文件夹。
- 缓存:通过使用WWW.LoadFromCacheOrDownload实现。AssetBundles会缓存到本地存储设备上的Unity缓存文件夹。WebPlayer共享缓存允许50MB大小的缓存。PC/Mac 和 iOS/Android 限制是4GB。
下面是一个非缓存的例子:
using System;
using UnityEngine;
using System.Collections;
class NonCachingLoadExample : MonoBehaviour {
public string BundleURL;
public string AssetName;
IEnumerator Start() {
// Download the file from the URL. It will not be saved in the Cache
using (WWW www = new WWW(BundleURL)) {
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" www.error);
AssetBundle bundle = www.assetBundle;
if (AssetName == "")
Instantiate(bundle.mainAsset);
else
Instantiate(bundle.LoadAsset(AssetName));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
}
// memory is freed from the web stream (www.Dispose() gets called implicitly)
}
}
一个使用 WWW.LoadFromCacheOrDownload 的缓存的例子:
using System;
using UnityEngine;
using System.Collections;
public class CachingLoadExample : MonoBehaviour {
public string BundleURL;
public string AssetName;
public int version;
void Start() {
StartCoroutine (DownloadAndCache());
}
IEnumerator DownloadAndCache (){
// Wait for the Caching system to be ready
while (!Caching.ready)
yield return null;
// Load the AssetBundle file from Cache if it exists with the same version
// or download and store it in the cache
using(WWW www = WWW.LoadFromCacheOrDownload (BundleURL, version)){
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" www.error);
AssetBundle bundle = www.assetBundle;
if (AssetName == "")
Instantiate(bundle.mainAsset);
else
Instantiate(bundle.LoadAsset(AssetName));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
} // memory is freed from the web stream (www.Dispose() gets called implicitly)
}
}
当你获取 .assetBundle 资源,下载的数据被提取并且 AssetBundle 对象会被创建。这时,你就可以加载bundle中包含的对象。LoadFromCacheOrDownload 第二个参数指定了要下载的AssetBundle的版本。如果缓存中没有或者版本比需要的低,就会下载AssetBundle。否则就直接从缓存中获取AssetBundle。
Editor环境下加载
Editor下加载AssetBundles比较麻烦,因为有资源更新时,还要重新打包AssetBundles。这时候通过 Resources.LoadAssetAtPath 直接获取Asset比较方便:
// C# Example
// Loading an Asset from disk instead of loading from an AssetBundle
// when running in the Editor
using System.Collections;
using UnityEngine;
class LoadAssetFromAssetBundle : MonoBehaviour
{
public Object Obj;
public IEnumerator DownloadAssetBundle(string asset, string url, int version) where T : Object {
Obj = null;
#if UNITY_EDITOR
Obj = Resources.LoadAssetAtPath("Assets/" asset, typeof(T));
yield return null;
#else
// Wait for the Caching system to be ready
while (!Caching.ready)
yield return null;
// Start the download
using(WWW www = WWW.LoadFromCacheOrDownload (url, version)){
yield return www;
if (www.error != null)
throw new Exception("WWW download:" www.error);
AssetBundle assetBundle = www.assetBundle;
Obj = assetBundle.LoadAsset(asset, typeof(T));
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
} // memory is freed from the web stream (www.Dispose() gets called implicitly)
#endif
}
}
加载和卸载AssetBundle中的对象
当从下载的数据创建了AssetBundle对象后,有三种方法加载AssetBundle中的资源:
- AssetBundle.LoadAsset,会使用名字作为参数加载物体,还有一个可选的参数指定加载类型。
- AssetBundle.LoadAssetAsync,效果和上面一样,只是它是异步的,加载时不会阻塞主线程。这对加载大资源或很多资源很有效。
- AssetBundle.LoadAllAssets,会加载AssetBundle中的所有物体,可以指定对象类型。
要卸载资源,需要使用 AssetBundle.Unload。这个方法有一个布尔参数,以指示是卸载所有数据(包括以加载的数据),还是只卸载下载的压缩文件。如果你正在使用来自AssetBundle的一些资源,而你又想释放一些内存,可以传递参数false来卸载压缩的资源文件。而如果你不再使用AssetBundle的任何资源,可以传递true来彻底卸载已经加载的资源。
异步加载AssetBundle的资源
using UnityEngine;
// Note: This example does not check for errors. Please look at the example in the DownloadingAssetBundles section for more information
IEnumerator Start () {
// Start a download of the given URL
WWW www = WWW.LoadFromCacheOrDownload (url, 1);
// Wait for download to complete
yield return www;
// Load and retrieve the AssetBundle
AssetBundle bundle = www.assetBundle;
// Load the object asynchronously
AssetBundleRequest request = bundle.LoadAssetAsync ("myObject", typeof(GameObject));
// Wait for completion
yield return request;
// Get the reference to the loaded object
GameObject obj = request.asset as GameObject;
// Unload the AssetBundles compressed contents to conserve memory
bundle.Unload(false);
// Frees the memory from the web stream
www.Dispose();
}
记录加载的AssetBundle
对于一个AssetBundle,Unity只允许同一时间加载一个实例。这意味着如果一个AssetBundle已经加载过了,且没有卸载,你就不能再用WWW获取它了。即当你试图使用如下方式获取一个已经加载过的AssetBundle:
AssetBundle bundle = www.assetBundle;
这会报错:Cannot load cached AssetBundle. A file of the same name is already loaded from another AssetBundle。 而且assetBundle会返回null。如果第一个资源还在加载中时,你是不能在第二个下载时获得AssetBundle的,因此你要做的要么就是在不需要时卸载AssetBundle,要么对它保持引用以避免当它已经存在于内存中时还下载它。
要注意再Unity5之前,在要卸载bundles之前,所有bundles的加载都要完成。
一般还是推荐在不需要时卸载AssetBundle,如果你一定要记录下载的AssetBundle,你可以使用一个包装类,如下所示:
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 dictAssetBundleRefs;
static AssetBundleManager (){
dictAssetBundleRefs = new Dictionary();
}
// 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是静态类,你记录的任何AssetBundles在加载新场景后都不会被销毁。这个类只是个参考,还是推荐一但不使用就马上卸载AssetBundles。
