异步加载AssetBundle方案,4.x版本资源加载

发表于2017-11-29
评论0 2.1k浏览

这篇文章给大家介绍的是异步加载AssetBundle方案,Unity4.x版本下的资源加载,不清楚的可以看看下面的实现方法。


提升资源加载速度,不卡主线程的加载方式

  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4.     public abstract class AssetBundleLoadOperation : IEnumerator  
  5.     {  
  6.         public object Current  
  7.         {  
  8.             get  
  9.             {  
  10.                 return null;  
  11.             }  
  12.         }  
  13.         public bool MoveNext()  
  14.         {  
  15.             return !IsDone();  
  16.         }  
  17.   
  18.         public void Reset()  
  19.         {  
  20.         }  
  21.   
  22.         abstract public bool Update();  
  23.   
  24.         abstract public bool IsDone();  
  25.     }  
  26.   
  27.     public class AssetBundleLoadLevelOperation : AssetBundleLoadOperation  
  28.     {  
  29.         protected string m_AssetBundleName;//资源名  
  30.         protected string m_LevelName;//场景名  
  31.         protected bool m_IsAdditive;  
  32.         protected string m_DownloadingError;//下载错误信息  
  33.         protected AsyncOperation m_Request;  
  34.  
  35.         #region//场景加载  
  36.         public AssetBundleLoadLevelOperation(string assetbundleName, string levelName, bool isAdditive)  
  37.         {  
  38.             m_AssetBundleName = assetbundleName;  
  39.             m_LevelName = levelName;  
  40.             m_IsAdditive = isAdditive;  
  41.         }  
  42.   
  43.         public override bool Update()  
  44.         {  
  45.             if (m_Request != null)  
  46.                 return false;  
  47.   
  48.             LoadedAssetBundle bundle = AssetBundleManager.GetLoadedAssetBundle(m_AssetBundleName, out m_DownloadingError);  
  49.             if (bundle != null)  
  50.             {  
  51.                 if (m_IsAdditive)  
  52.                     m_Request = Application.LoadLevelAdditiveAsync(m_LevelName);  
  53.                 else  
  54.                     m_Request = Application.LoadLevelAsync(m_LevelName);  
  55.                 return false;  
  56.             }  
  57.             else  
  58.                 return true;  
  59.         }  
  60.   
  61.         public override bool IsDone()  
  62.         {  
  63.             // Return if meeting downloading error.  
  64.             // m_DownloadingError might come from the dependency downloading.  
  65.             if (m_Request == null && m_DownloadingError != null)  
  66.             {  
  67.                 Debug.LogError(m_DownloadingError);  
  68.                 return true;  
  69.             }  
  70.   
  71.             return m_Request != null && m_Request.isDone;  
  72.         }  
  73.     }  
  74.         #endregion  
  75.   
  76.     public abstract class AssetBundleLoadAssetOperation : AssetBundleLoadOperation  
  77.     {  
  78.         public abstract T GetAsset<T>() where T : UnityEngine.Object;  
  79.     }  
  80.   
  81.     public class AssetBundleLoadAssetOperationFull : AssetBundleLoadAssetOperation  
  82.     {  
  83.         protected string m_AssetBundleName;  
  84.         protected string m_DownloadingError;  
  85.         protected System.Type m_Type;  
  86.         protected AssetBundleRequest m_Request = null;  
  87.   
  88.         public AssetBundleLoadAssetOperationFull(string bundleName, System.Type type)  
  89.         {  
  90.             m_AssetBundleName = bundleName;  
  91.             m_Type = type;  
  92.         }  
  93.   
  94.         public override T GetAsset<T>()  
  95.         {  
  96.             if (m_Request != null && m_Request.isDone)  
  97.                 return m_Request.asset as T;  
  98.             else  
  99.                 return null;  
  100.         }  
  101.   
  102.         // Returns true if more Update calls are required.  
  103.         public override bool Update()  
  104.         {  
  105.             if (m_Request != null)  
  106.                 return false;  
  107.   
  108.             LoadedAssetBundle bundle = AssetBundleManager.GetLoadedAssetBundle(m_AssetBundleName, out m_DownloadingError);  
  109.             if (bundle != null)  
  110.             {  
  111.                 ///@TODO: When asset bundle download fails this throws an exception...  
  112.                 m_Request = bundle.m_AssetBundle.LoadAllAssetsAsync(m_Type);  
  113.                 return false;  
  114.             }  
  115.             else  
  116.             {  
  117.                 return true;  
  118.             }  
  119.         }  
  120.   
  121.         public override bool IsDone()  
  122.         {  
  123.             // Return if meeting downloading error.  
  124.             // m_DownloadingError might come from the dependency downloading.  
  125.             if (m_Request == null && m_DownloadingError != null)  
  126.             {  
  127.                 Debug.LogError(m_DownloadingError);  
  128.                 return true;  
  129.             }  
  130.   
  131.             return m_Request != null && m_Request.isDone;  
  132.         }  
  133.     }  
  1. using System.Collections;  
  2. using System.Collections.Generic;  
  3. using UnityEngine;  
  4.   
  5. public class LoadedAssetBundle  
  6. {  
  7.     public AssetBundle m_AssetBundle;//assetbundle资源  
  8.     public int m_ReferencedCount;//引用数量  
  9.   
  10.     public LoadedAssetBundle(AssetBundle assetBundle)  
  11.     {  
  12.         m_AssetBundle = assetBundle;  
  13.         m_ReferencedCount = 1;  
  14.     }  
  15. }  
  16. public class AssetBundleManager : MonoBehaviour {  
  17.   
  18.     static string m_BaseDownloadingURL = "";//加载地址(采用下载地址+文件名)  
  19.   
  20.     static Dictionary<string, LoadedAssetBundle> m_LoadedAssetBundles = new Dictionary<string, LoadedAssetBundle>();     //下载的资源  
  21.     static Dictionary<string, WWW> m_DownloadingWWWs = new Dictionary<string, WWW>();                                    //下载列表  
  22.     static Dictionary<stringstring> m_DownloadingErrors = new Dictionary<stringstring>();                            //下载错误信息  
  23.     static List<AssetBundleLoadOperation> m_InProgressOperations = new List<AssetBundleLoadOperation>();                 //进度信息  
  24.     //static Dictionary<string, string[]> m_Dependencies = new Dictionary<string, string[]>();                             //依赖信息  
  25.   
  26.     //加载地址  
  27.     public static string BaseDownloadingURL  
  28.     {  
  29.         get { return m_BaseDownloadingURL; }  
  30.         set { m_BaseDownloadingURL = value; }  
  31.     }  
  32.     //获取StreamingAssets路径  
  33.     private static string GetStreamingAssetsPath()  
  34.     {  
  35.         if (Application.isEditor)  
  36.             return "file://" + System.Environment.CurrentDirectory.Replace("\\", "/"); // Use the build output folder directly.  
  37.         else if (Application.isWebPlayer)  
  38.             return System.IO.Path.GetDirectoryName(Application.absoluteURL).Replace("\\", "/") + "/StreamingAssets";  
  39.         else if (Application.isMobilePlatform || Application.isConsolePlatform)  
  40.             return Application.streamingAssetsPath;  
  41.         else // For standalone player.  
  42.             return "file://" + Application.streamingAssetsPath;  
  43.     }  
  44.     //获取已加载资源  
  45.     static public LoadedAssetBundle GetLoadedAssetBundle(string assetBundleName, out string error)  
  46.     {  
  47.         if (m_DownloadingErrors.TryGetValue(assetBundleName, out error))  
  48.             return null;  
  49.   
  50.         LoadedAssetBundle bundle = null;  
  51.         m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);  
  52.         if (bundle == null)  
  53.             return null;  
  54.   
  55.         // No dependencies are recorded, only the bundle itself is required.  
  56.         //string[] dependencies = null;  
  57.         //if (!m_Dependencies.TryGetValue(assetBundleName, out dependencies))  
  58.         //    return bundle;  
  59.   
  60.         // Make sure all dependencies are loaded  
  61.         //foreach (var dependency in dependencies)  
  62.         //{  
  63.         //    if (m_DownloadingErrors.TryGetValue(assetBundleName, out error))  
  64.         //        return bundle;  
  65.   
  66.         //    // Wait all the dependent assetBundles being loaded.  
  67.         //    LoadedAssetBundle dependentBundle;  
  68.         //    m_LoadedAssetBundles.TryGetValue(dependency, out dependentBundle);  
  69.         //    if (dependentBundle == null)  
  70.         //        return null;  
  71.         //}  
  72.   
  73.         return bundle;  
  74.     }  
  75.     //释放资源  
  76.     static protected void UnloadAssetBundleInternal(string assetBundleName)  
  77.     {  
  78.         string error;  
  79.         LoadedAssetBundle bundle = GetLoadedAssetBundle(assetBundleName, out error);  
  80.         if (bundle == null)  
  81.             return;  
  82.   
  83.         if (--bundle.m_ReferencedCount == 0)  
  84.         {  
  85.             bundle.m_AssetBundle.Unload(false);  
  86.             m_LoadedAssetBundles.Remove(assetBundleName);  
  87.         }  
  88.     }  
  89.     //下载列表下载  
  90.     void Update()  
  91.     {  
  92.         // Collect all the finished WWWs.  
  93.         var keysToRemove = new List<string>();  
  94.         foreach (var keyValue in m_DownloadingWWWs)  
  95.         {  
  96.             WWW download = keyValue.Value;  
  97.   
  98.             // If downloading fails.  
  99.             if (download.error != null)  
  100.             {  
  101.                 m_DownloadingErrors.Add(keyValue.Key, string.Format("Failed downloading bundle {0} from {1}: {2}", keyValue.Key, download.url, download.error));  
  102.                 keysToRemove.Add(keyValue.Key);  
  103.                 continue;  
  104.             }  
  105.   
  106.             // If downloading succeeds.  
  107.             if (download.isDone)  
  108.             {  
  109.                 AssetBundle bundle = download.assetBundle;  
  110.                 if (bundle == null)  
  111.                 {  
  112.                     m_DownloadingErrors.Add(keyValue.Key, string.Format("{0} is not a valid asset bundle.", keyValue.Key));  
  113.                     keysToRemove.Add(keyValue.Key);  
  114.                     continue;  
  115.                 }  
  116.   
  117.                 //Debug.Log("Downloading " + keyValue.Key + " is done at frame " + Time.frameCount);  
  118.                 m_LoadedAssetBundles.Add(keyValue.Key, new LoadedAssetBundle(download.assetBundle));  
  119.                 keysToRemove.Add(keyValue.Key);  
  120.             }  
  121.         }  
  122.   
  123.         // Remove the finished WWWs.  
  124.         foreach (var key in keysToRemove)  
  125.         {  
  126.             WWW download = m_DownloadingWWWs[key];  
  127.             m_DownloadingWWWs.Remove(key);  
  128.             download.Dispose();  
  129.         }  
  130.   
  131.         // Update all in progress operations  
  132.         for (int i = 0; i < m_InProgressOperations.Count; )  
  133.         {  
  134.             if (!m_InProgressOperations[i].Update())  
  135.             {  
  136.                 m_InProgressOperations.RemoveAt(i);  
  137.             }  
  138.             else  
  139.                 i++;  
  140.         }  
  141.     }  
  142.     //内部获取资源  
  143.     static protected bool LoadAssetBundleInternal(string assetBundleName)  
  144.     {  
  145.         // Already loaded.  
  146.         LoadedAssetBundle bundle = null;  
  147.         m_LoadedAssetBundles.TryGetValue(assetBundleName, out bundle);  
  148.         if (bundle != null)  
  149.         {  
  150.             bundle.m_ReferencedCount++;  
  151.             return true;  
  152.         }  
  153.   
  154.         // @TODO: Do we need to consider the referenced count of WWWs?  
  155.         // In the demo, we never have duplicate WWWs as we wait LoadAssetAsync()/LoadLevelAsync() to be finished before calling another LoadAssetAsync()/LoadLevelAsync().  
  156.         // But in the real case, users can call LoadAssetAsync()/LoadLevelAsync() several times then wait them to be finished which might have duplicate WWWs.  
  157.         if (m_DownloadingWWWs.ContainsKey(assetBundleName))  
  158.             return true;  
  159.   
  160.         WWW download = null;  
  161.         string url = m_BaseDownloadingURL + assetBundleName;//基础路径加上文件名  
  162.         download = new WWW(url);  
  163.   
  164.         m_DownloadingWWWs.Add(assetBundleName, download);  
  165.   
  166.         return false;  
  167.     }  
  168.   
  169.     // Load AssetBundle and its dependencies.加载资源  
  170.     static protected void LoadAssetBundle(string assetBundleName)  
  171.     {  
  172.         // Check if the assetBundle has already been processed.  
  173.         bool isAlreadyProcessed = LoadAssetBundleInternal(assetBundleName);  
  174.     }  
  175.   
  176.     // Load asset from the given assetBundle.异步加载资源  
  177.     static public AssetBundleLoadAssetOperation LoadAssetAsync(string assetBundleName, System.Type type)  
  178.     {  
  179.   
  180.         AssetBundleLoadAssetOperation operation = null;  
  181.         {  
  182.             LoadAssetBundle(assetBundleName);  
  183.             operation = new AssetBundleLoadAssetOperationFull(assetBundleName, type);  
  184.   
  185.             m_InProgressOperations.Add(operation);  
  186.         }  
  187.   
  188.         return operation;  
  189.     }  
  190.  
  191.     #region//单例  
  192.     // 定义一个静态变量来保存类的实例    
  193.         private static AssetBundleManager instance;    
  194.         // 定义一个标识确保线程同步    
  195.         private static readonly object locker = new object();    
  196.         // 定义私有构造函数,使外界不能创建该类实例    
  197.         private AssetBundleManager()    
  198.         {    
  199.         }    
  200.         /// <summary>    
  201.         /// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点    
  202.         /// </summary>    
  203.         /// <returns></returns>    
  204.         public static AssetBundleManager Instance()    
  205.         {    
  206.             // 当第一个线程运行到这里时,此时会对locker对象 "加锁",    
  207.             // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁    
  208.             // lock语句运行完之后(即线程运行完之后)会对该对象"解锁"    
  209.             lock (locker)    
  210.             {    
  211.                 // 如果类的实例不存在则创建,否则直接返回    
  212.                 if (instance == null)    
  213.                 {  
  214.                     GameObject tGO = new GameObject("AssetBundleManager");  
  215.                     tGO.AddComponent<AssetBundleManager>();  
  216.                     instance = tGO.GetComponent<AssetBundleManager>();  
  217.                     instance = new AssetBundleManager();  
  218.                     Debug.Log("@@ Creat AssetBundleManager");  
  219.                 }    
  220.             }  
  221.             return instance;    
  222.         }    
  223.     #endregion  
  224.   
  225. }  

加载案例:
  1. void Start()  
  2.    {  
  3.        //异步加载资源+  
  4.        AssetBundleManager.Instance();  
  5.        AssetBundleManager.BaseDownloadingURL = PathManager.Instance().GetPath() + "101/New Folder/";  
  6.        Debug.Log(AssetBundleManager.BaseDownloadingURL);  
  7.        StartCoroutine(InstantiateGameObjectAsync("Cube.u3d"));  
  8.          
  9.    }  
  10.   
  11.   
  12.    protected IEnumerator InstantiateGameObjectAsync(string assetBundleName)  
  13.    {  
  14.        // This is simply to get the elapsed time for this phase of AssetLoading.  
  15.        float startTime = Time.realtimeSinceStartup;  
  16.   
  17.        // Load asset from assetBundle.  
  18.        AssetBundleLoadAssetOperation request = AssetBundleManager.LoadAssetAsync(assetBundleName, typeof(GameObject));  
  19.        if (request == null)  
  20.        {  
  21.            Debug.Log("request null");  
  22.            yield break; }  
  23.        yield return StartCoroutine(request);  
  24.   
  25.        // Get the asset.  
  26.        var prefab = request.GetAsset<GameObject>();  
  27.   
  28.        if (prefab != null)  
  29.            GameObject.Instantiate(prefab);  
  30.   
  31.        // Calculate and display the elapsed time.  
  32.        float elapsedTime = Time.realtimeSinceStartup - startTime;  
  33.        Debug.Log((prefab == null ? " was not" : " was") + " loaded successfully in " + elapsedTime + " seconds");  
  34.    }  

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论