异步加载AssetBundle方案,4.x版本资源加载
这篇文章给大家介绍的是异步加载AssetBundle方案,Unity4.x版本下的资源加载,不清楚的可以看看下面的实现方法。
提升资源加载速度,不卡主线程的加载方式
- using UnityEngine;
- using System.Collections;
- public abstract class AssetBundleLoadOperation : IEnumerator
- {
- public object Current
- {
- get
- {
- return null;
- }
- }
- public bool MoveNext()
- {
- return !IsDone();
- }
- public void Reset()
- {
- }
- abstract public bool Update();
- abstract public bool IsDone();
- }
- public class AssetBundleLoadLevelOperation : AssetBundleLoadOperation
- {
- protected string m_AssetBundleName;//资源名
- protected string m_LevelName;//场景名
- protected bool m_IsAdditive;
- protected string m_DownloadingError;//下载错误信息
- protected AsyncOperation m_Request;
- #region//场景加载
- public AssetBundleLoadLevelOperation(string assetbundleName, string levelName, bool isAdditive)
- {
- m_AssetBundleName = assetbundleName;
- m_LevelName = levelName;
- m_IsAdditive = isAdditive;
- }
- public override bool Update()
- {
- if (m_Request != null)
- return false;
- LoadedAssetBundle bundle = AssetBundleManager.GetLoadedAssetBundle(m_AssetBundleName, out m_DownloadingError);
- if (bundle != null)
- {
- if (m_IsAdditive)
- m_Request = Application.LoadLevelAdditiveAsync(m_LevelName);
- else
- m_Request = Application.LoadLevelAsync(m_LevelName);
- return false;
- }
- else
- return true;
- }
- public override bool IsDone()
- {
- // Return if meeting downloading error.
- // m_DownloadingError might come from the dependency downloading.
- if (m_Request == null && m_DownloadingError != null)
- {
- Debug.LogError(m_DownloadingError);
- return true;
- }
- return m_Request != null && m_Request.isDone;
- }
- }
- #endregion
- public abstract class AssetBundleLoadAssetOperation : AssetBundleLoadOperation
- {
- public abstract T GetAsset<T>() where T : UnityEngine.Object;
- }
- public class AssetBundleLoadAssetOperationFull : AssetBundleLoadAssetOperation
- {
- protected string m_AssetBundleName;
- protected string m_DownloadingError;
- protected System.Type m_Type;
- protected AssetBundleRequest m_Request = null;
- public AssetBundleLoadAssetOperationFull(string bundleName, System.Type type)
- {
- m_AssetBundleName = bundleName;
- m_Type = type;
- }
- public override T GetAsset<T>()
- {
- if (m_Request != null && m_Request.isDone)
- return m_Request.asset as T;
- else
- return null;
- }
- // Returns true if more Update calls are required.
- public override bool Update()
- {
- if (m_Request != null)
- return false;
- LoadedAssetBundle bundle = AssetBundleManager.GetLoadedAssetBundle(m_AssetBundleName, out m_DownloadingError);
- if (bundle != null)
- {
- ///@TODO: When asset bundle download fails this throws an exception...
- m_Request = bundle.m_AssetBundle.LoadAllAssetsAsync(m_Type);
- return false;
- }
- else
- {
- return true;
- }
- }
- public override bool IsDone()
- {
- // Return if meeting downloading error.
- // m_DownloadingError might come from the dependency downloading.
- if (m_Request == null && m_DownloadingError != null)
- {
- Debug.LogError(m_DownloadingError);
- return true;
- }
- return m_Request != null && m_Request.isDone;
- }
- }
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class LoadedAssetBundle
- {
- public AssetBundle m_AssetBundle;//assetbundle资源
- public int m_ReferencedCount;//引用数量
- public LoadedAssetBundle(AssetBundle assetBundle)
- {
- m_AssetBundle = assetBundle;
- m_ReferencedCount = 1;
- }
- }
- public class AssetBundleManager : MonoBehaviour {
- static string m_BaseDownloadingURL = "";//加载地址(采用下载地址+文件名)
- static Dictionary<string, LoadedAssetBundle> m_LoadedAssetBundles = new Dictionary<string, LoadedAssetBundle>(); //下载的资源
- static Dictionary<string, WWW> m_DownloadingWWWs = new Dictionary<string, WWW>(); //下载列表
- static Dictionary<string, string> m_DownloadingErrors = new Dictionary<string, string>(); //下载错误信息
- static List<AssetBundleLoadOperation> m_InProgressOperations = new List<AssetBundleLoadOperation>(); //进度信息
- //static Dictionary<string, string[]> m_Dependencies = new Dictionary<string, string[]>(); //依赖信息
- //加载地址
- public static string BaseDownloadingURL
- {
- get { return m_BaseDownloadingURL; }
- set { m_BaseDownloadingURL = value; }
- }
- //获取StreamingAssets路径
- private static string GetStreamingAssetsPath()
- {
- if (Application.isEditor)
- return "file://" + System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.
- else if (Application.isWebPlayer)
- return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";
- else if (Application.isMobilePlatform || Application.isConsolePlatform)
- return Application.streamingAssetsPath;
- else // For standalone player.
- return "file://" + Application.streamingAssetsPath;
- }
- //获取已加载资源
- static public LoadedAssetBundle GetLoadedAssetBundle(string assetBundleName, out string error)
- {
- if (m_DownloadingErrors.TryGetValue(assetBundleName, out error))
- return null;
- LoadedAssetBundle bundle = null;
- m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);
- if (bundle == null)
- return null;
- // No dependencies are recorded, only the bundle itself is required.
- //string[] dependencies = null;
- //if (!m_Dependencies.TryGetValue(assetBundleName, out dependencies))
- // return bundle;
- // Make sure all dependencies are loaded
- //foreach (var dependency in dependencies)
- //{
- // if (m_DownloadingErrors.TryGetValue(assetBundleName, out error))
- // return bundle;
- // // Wait all the dependent assetBundles being loaded.
- // LoadedAssetBundle dependentBundle;
- // m_LoadedAssetBundles.TryGetValue(dependency, out dependentBundle);
- // if (dependentBundle == null)
- // return null;
- //}
- return bundle;
- }
- //释放资源
- static protected void UnloadAssetBundleInternal(string assetBundleName)
- {
- string error;
- LoadedAssetBundle bundle = GetLoadedAssetBundle(assetBundleName, out error);
- if (bundle == null)
- return;
- if (--bundle.m_ReferencedCount == 0)
- {
- bundle.m_AssetBundle.Unload(false);
- m_LoadedAssetBundles.Remove(assetBundleName);
- }
- }
- //下载列表下载
- void Update()
- {
- // Collect all the finished WWWs.
- var keysToRemove = new List<string>();
- foreach (var keyValue in m_DownloadingWWWs)
- {
- WWW download = keyValue.Value;
- // If downloading fails.
- if (download.error != null)
- {
- m_DownloadingErrors.Add(keyValue.Key, string.Format("Failed downloading bundle {0} from {1}: {2}", keyValue.Key, download.url, download.error));
- keysToRemove.Add(keyValue.Key);
- continue;
- }
- // If downloading succeeds.
- if (download.isDone)
- {
- AssetBundle bundle = download.assetBundle;
- if (bundle == null)
- {
- m_DownloadingErrors.Add(keyValue.Key, string.Format("{0} is not a valid asset bundle.", keyValue.Key));
- keysToRemove.Add(keyValue.Key);
- continue;
- }
- //Debug.Log("Downloading " + keyValue.Key + " is done at frame " + Time.frameCount);
- m_LoadedAssetBundles.Add(keyValue.Key, new LoadedAssetBundle(download.assetBundle));
- keysToRemove.Add(keyValue.Key);
- }
- }
- // Remove the finished WWWs.
- foreach (var key in keysToRemove)
- {
- WWW download = m_DownloadingWWWs[key];
- m_DownloadingWWWs.Remove(key);
- download.Dispose();
- }
- // Update all in progress operations
- for (int i = 0; i < m_InProgressOperations.Count; )
- {
- if (!m_InProgressOperations[i].Update())
- {
- m_InProgressOperations.RemoveAt(i);
- }
- else
- i++;
- }
- }
- //内部获取资源
- static protected bool LoadAssetBundleInternal(string assetBundleName)
- {
- // Already loaded.
- LoadedAssetBundle bundle = null;
- m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);
- if (bundle != null)
- {
- bundle.m_ReferencedCount++;
- return true;
- }
- // @TODO: Do we need to consider the referenced count of WWWs?
- // In the demo, we never have duplicate WWWs as we wait LoadAssetAsync()/LoadLevelAsync() to be finished before calling another LoadAssetAsync()/LoadLevelAsync().
- // But in the real case, users can call LoadAssetAsync()/LoadLevelAsync() several times then wait them to be finished which might have duplicate WWWs.
- if (m_DownloadingWWWs.ContainsKey(assetBundleName))
- return true;
- WWW download = null;
- string url = m_BaseDownloadingURL + assetBundleName;//基础路径加上文件名
- download = new WWW(url);
- m_DownloadingWWWs.Add(assetBundleName, download);
- return false;
- }
- // Load AssetBundle and its dependencies.加载资源
- static protected void LoadAssetBundle(string assetBundleName)
- {
- // Check if the assetBundle has already been processed.
- bool isAlreadyProcessed = LoadAssetBundleInternal(assetBundleName);
- }
- // Load asset from the given assetBundle.异步加载资源
- static public AssetBundleLoadAssetOperation LoadAssetAsync(string assetBundleName, System.Type type)
- {
- AssetBundleLoadAssetOperation operation = null;
- {
- LoadAssetBundle(assetBundleName);
- operation = new AssetBundleLoadAssetOperationFull(assetBundleName, type);
- m_InProgressOperations.Add(operation);
- }
- return operation;
- }
- #region//单例
- // 定义一个静态变量来保存类的实例
- private static AssetBundleManager instance;
- // 定义一个标识确保线程同步
- private static readonly object locker = new object();
- // 定义私有构造函数,使外界不能创建该类实例
- private AssetBundleManager()
- {
- }
- /// <summary>
- /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
- /// </summary>
- /// <returns></returns>
- public static AssetBundleManager Instance()
- {
- // 当第一个线程运行到这里时,此时会对locker对象 "加锁",
- // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
- // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
- lock (locker)
- {
- // 如果类的实例不存在则创建,否则直接返回
- if (instance == null)
- {
- GameObject tGO = new GameObject("AssetBundleManager");
- tGO.AddComponent<AssetBundleManager>();
- instance = tGO.GetComponent<AssetBundleManager>();
- instance = new AssetBundleManager();
- Debug.Log("@@ Creat AssetBundleManager");
- }
- }
- return instance;
- }
- #endregion
- }
加载案例:
- void Start()
- {
- //异步加载资源+
- AssetBundleManager.Instance();
- AssetBundleManager.BaseDownloadingURL = PathManager.Instance().GetPath() + "101/New Folder/";
- Debug.Log(AssetBundleManager.BaseDownloadingURL);
- StartCoroutine(InstantiateGameObjectAsync("Cube.u3d"));
- }
- protected IEnumerator InstantiateGameObjectAsync(string assetBundleName)
- {
- // This is simply to get the elapsed time for this phase of AssetLoading.
- float startTime = Time.realtimeSinceStartup;
- // Load asset from assetBundle.
- AssetBundleLoadAssetOperation request = AssetBundleManager.LoadAssetAsync(assetBundleName, typeof(GameObject));
- if (request == null)
- {
- Debug.Log("request null");
- yield break; }
- yield return StartCoroutine(request);
- // Get the asset.
- var prefab = request.GetAsset<GameObject>();
- if (prefab != null)
- GameObject.Instantiate(prefab);
- // Calculate and display the elapsed time.
- float elapsedTime = Time.realtimeSinceStartup - startTime;
- Debug.Log((prefab == null ? " was not" : " was") + " loaded successfully in " + elapsedTime + " seconds");
- }