Unity5系列资源管理AssetBundle——加载
上次我们进行了AssetBundle打包,现在我们还把打包的资源加载到我们的游戏中。
在加载之前,我们需要把打包好的Bundle包裹放到服务器上,如果没有,也可以使用XAMPP搭建本地服务器。
加载的AssetBundle文件是放在内存中的,所以如果没有很好的管理,会使游戏性能大打折扣,因此,我们在这里最好使用缓存策略。
我们的AssetBundle:
1 using System; 2 using UnityEngine; 3 4 namespace MrCAssetFramework 5 { 6 public sealed class MrCAssetBundle 7 { 8 internal AssetBundle m_AssetBundle; 9 internal string m_AssetBundleName; 10 internal MrCAssetBundle(AssetBundle assetBundle,string name) 11 { 12 this.m_AssetBundle = assetBundle; 13 this.m_AssetBundleName = name; 14 this.m_ReferencedCount = 1; 15 } 16 17 #region ReferenceCountManage 18 //yinyongjishujizhi 19 private int m_ReferencedCount; 20 public void Retain() 21 { 22 this.m_ReferencedCount++; 23 } 24 public void Release(){ 25 this.m_ReferencedCount--; 26 //当引用计数为0时,卸载资源 27 if (this.m_ReferencedCount == 0) 28 { 29 this.m_AssetBundle.Unload (true); 30 MrCAssetCache.FreeBundle(this.m_AssetBundleName); 31 } 32 } 33 34 public int RetainCount() 35 { 36 return this.m_ReferencedCount; 37 } 38 #endregion 39 40 } 41 }
AssetBundle管理:
1 using System; 2 using UnityEngine; 3 using System.Collections; 4 using System.Collections.Generic; 5 6 namespace MrCAssetFramework 7 { 8 public class MrCAssetManager:MonoBehaviour 9 { 10 11 #region Singleton 12 private static GameObject s_go_MrCAssetManager = null; 13 private static MrCAssetManager s_MrCAssetManager = null; 14 15 public static MrCAssetManager DefaultAssetManager{ 16 get{ 17 if (s_MrCAssetManager == null) { 18 s_go_MrCAssetManager = new GameObject ("LOAssetManager"); 19 s_MrCAssetManager = s_go_MrCAssetManager.AddComponent<MrCAssetManager> (); 20 } 21 22 return s_MrCAssetManager; 23 } 24 } 25 26 #endregion 27 28 public static string URI{ set; get;} 29 public static string ManifestName{ set; get;} 30 //无返回值泛型委托.. 31 public static Action<bool> InitBlock; 32 33 public AssetBundleManifest manifest{ set; get;} 34 35 36 ///<summary> 37 /// To get the asset.. 38 /// </summary> 39 40 /// <returns>The asset..</returns> 41 public T GetAsset<T>(string assetbundlename,string assetname) where T:UnityEngine.Object 42 { 43 MrCAssetBundle lab = MrCAssetCache.GetBundleCache(assetbundlename); 44 45 if (lab == null) { 46 return null; 47 } 48 else 49 { 50 return lab.m_AssetBundle.LoadAsset<T>(assetname); 51 } 52 } 53 54 IEnumerator LoadManifestBundle() 55 { 56 //缓存中已经存在请求的bundle,中止.. 57 if (MrCAssetCache.InCache(MrCAssetManager.ManifestName)) { 58 yield break; 59 } 60 61 // 通过网络下载AssetBundle 62 WWW www = IsLoadAssetBundleAtInternal(MrCAssetManager.ManifestName); 63 yield return www; 64 65 this.manifest = this.GetAsset<AssetBundleManifest>(MrCAssetManager.ManifestName,"AssetBundleManifest"); 66 MrCAssetManager.InitBlock (this.manifest != null); 67 } 68 void Start() 69 { 70 StartCoroutine ("LoadManifestBundle"); 71 } 72 73 74 75 76 #region 加载包裹系列函数 77 78 79 ///<summary> 80 /// 检查是否已经从网络下载 81 /// </summary> 82 83 protected WWW IsLoadAssetBundleAtInternal (string assetBundleName) 84 { 85 //已经存在了呀 86 MrCAssetBundle bundle = MrCAssetCache.GetBundleCache(assetBundleName); 87 if (bundle != null) 88 { 89 //保留数加一次 90 bundle.Retain (); 91 return null; 92 } 93 94 //如果WWW缓存策略中包含有对应的关键字,则返回true 95 if (MrCAssetCache.InCache (assetBundleName)) { 96 return null; 97 } 98 99 //创建下载链接 100 WWW www = new WWW(MrCAssetManager.URI + assetBundleName); 101 // 按版本号,按需要通过网络下载AssetBundle,一般在正式游戏版本中,不使用上面的,因为它会每次打开游戏重新下载 102 //WWW www = WWW.LoadFromCacheOrDownload(LOAssetManager.URI + assetBundleName, nowVersion); 103 104 //加入缓存策略 105 MrCAssetCache.SetWWWCache(assetBundleName,www); 106 107 return www; 108 } 109 110 111 IEnumerator LoadDependencies(string assetBundleName) 112 { 113 if (this.manifest == null) { 114 yield return null; 115 } 116 // 获取依赖包裹 117 string[] dependencies = this.manifest.GetAllDependencies(assetBundleName); 118 119 Debug.Log(dependencies.Length); 120 121 if (dependencies.Length == 0) 122 { 123 yield return null; 124 } 125 126 // 记录并且加载所有的依赖包裹 127 MrCAssetCache.SetDependCache(assetBundleName, dependencies); 128 129 for (int i = 0; i < dependencies.Length; i++) 130 { 131 yield return IsLoadAssetBundleAtInternal (dependencies [i]); 132 } 133 } 134 135 136 /// <summary> 137 /// 加载资源包 138 /// </summary> 139 140 IEnumerator LoadAssetBundle(string assetBundleName) 141 { 142 if (MrCAssetCache.InCache(assetBundleName)) { 143 yield break; 144 } 145 // 通过网络下载AssetBundle 146 WWW www = IsLoadAssetBundleAtInternal(assetBundleName); 147 yield return www; 148 149 // 通过网络加载失败,下载依赖包裹 150 yield return StartCoroutine(LoadDependencies(assetBundleName)); 151 } 152 153 154 ///<summary> 155 /// 异步加载资源 156 /// </summary> 157 158 public IEnumerator LoadAssetAsync (string assetBundleName, string assetName, System.Type type) 159 { 160 //开始加载包裹 161 yield return StartCoroutine(LoadAssetBundle (assetBundleName)); 162 } 163 164 165 ///<summary> 166 /// 异步加载场景 167 /// </summary> 168 169 public IEnumerator LoadLevelAsync (string assetBundleName, string levelName, bool isAdditive) 170 { 171 //加载资源包 172 yield return StartCoroutine(LoadAssetBundle (assetBundleName)); 173 174 } 175 #endregion 176 177 178 #region Update 179 180 void Update() 181 { 182 MrCAssetCache.Update(); 183 } 184 #endregion 185 } 186 }
缓存管理:
1 using System; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 namespace MrCAssetFramework 6 { 7 internal sealed class MrCAssetCache 8 { 9 #region 包裹缓存机制 10 //创建缓存字典 11 private static Dictionary<string, MrCAssetBundle> assetBundleCache; 12 //缓存字典属性 13 private static Dictionary<string, MrCAssetBundle> BundleCache 14 { 15 get{ 16 if (assetBundleCache == null) { 17 assetBundleCache = new Dictionary<string, MrCAssetBundle> (); 18 } 19 20 return assetBundleCache; 21 } 22 } 23 24 //创建缓存WWW对象 25 private static Dictionary<string, WWW> wwwCache; 26 //创建缓存WWW对象属性 27 private static Dictionary<string, WWW> WwwCache{ 28 get{ 29 if (wwwCache == null) { 30 wwwCache = new Dictionary<string, WWW> (); 31 } 32 33 return wwwCache; 34 } 35 } 36 //创建依赖缓存对象 37 private static Dictionary<string, string[]> dependCache; 38 //创建依赖缓存属性 39 private static Dictionary<string, string[]> DependCache 40 { 41 get{ 42 if (dependCache == null) { 43 dependCache = new Dictionary<string, string[]> (); 44 } 45 return dependCache; 46 } 47 } 48 49 private static Dictionary<string, string> errorCache; 50 private static Dictionary<string,string> ErrorCache{ 51 get{ 52 if (errorCache == null) { 53 errorCache = new Dictionary<string, string> (); 54 } 55 return errorCache; 56 } 57 } 58 59 60 ///<summary> 61 /// Instantiate the cache. 62 /// </summary> 63 64 /// <returns><c>true</c>, if cache was ined, <c>false</c> otherwise.</returns> 65 /// <param name="assetbundlename">Assetbundlename.</param> 66 internal static bool InCache(string assetbundlename) 67 { 68 return MrCAssetCache.BundleCache.ContainsKey(assetbundlename); 69 } 70 #endregion 71 72 73 #region 卸载系列函数 74 75 76 ///<summary> 77 /// 卸载资源包和依赖包 78 /// </summary> 79 80 /// <param name="assetBundleName">Asset bundle name.</param> 81 public static void UnloadAssetBundle(string assetBundleName) 82 { 83 UnloadAssetBundleInternal (assetBundleName); 84 UnloadDependencies (assetBundleName); 85 } 86 internal static void UnloadDependencies(string assetBundleName) 87 { 88 string[] dependencies = null; 89 //获取所有的依赖包名称 90 if (!MrCAssetCache.DependCache.TryGetValue(assetBundleName, out dependencies) ) 91 return; 92 93 //卸载依赖包 94 foreach(var dependency in dependencies) 95 { 96 UnloadAssetBundleInternal(dependency); 97 } 98 //删除依赖缓存策略 99 MrCAssetCache.DependCache.Remove(assetBundleName); 100 } 101 102 internal static void UnloadAssetBundleInternal(string assetBundleName) 103 { 104 MrCAssetBundle bundle; 105 MrCAssetCache.BundleCache.TryGetValue(assetBundleName,out bundle); 106 107 if (bundle == null) 108 { 109 return; 110 } 111 bundle.Release (); 112 } 113 #endregion 114 115 #region GetFunction 116 internal static WWW GetWWWCache(string key) 117 { 118 WWW www; 119 120 MrCAssetCache.WwwCache.TryGetValue(key,out www); 121 122 return www; 123 } 124 internal static void SetWWWCache(string key,WWW value) 125 { 126 MrCAssetCache.WwwCache.Add(key,value); 127 } 128 129 internal static MrCAssetBundle GetBundleCache(string key) 130 { 131 MrCAssetBundle ab; 132 133 MrCAssetCache.BundleCache.TryGetValue(key,out ab); 134 135 return ab; 136 } 137 internal static void SetBundleCache(string key,MrCAssetBundle value) 138 { 139 MrCAssetCache.BundleCache.Add(key,value); 140 } 141 142 internal static string[] GetDependCache(string key) 143 { 144 string[] depends; 145 146 MrCAssetCache.DependCache.TryGetValue(key,out depends); 147 148 return depends; 149 } 150 internal static void SetDependCache(string key,string[] value) 151 { 152 MrCAssetCache.DependCache.Add(key,value); 153 } 154 155 internal static string GetErrorCache(string key) 156 { 157 string error; 158 159 MrCAssetCache.ErrorCache.TryGetValue(key,out error); 160 161 return error; 162 } 163 internal static void SetErrorCache(string key,string value) 164 { 165 MrCAssetCache.ErrorCache.Add(key,value); 166 } 167 #endregion 168 169 internal static void FreeBundle(string key) 170 { 171 MrCAssetCache.BundleCache.Remove(key); 172 } 173 174 #region Update 175 176 internal static void Update() 177 { 178 // Collect all the finished WWWs. 179 var keysToRemove = new List<string>(); 180 foreach (var keyValue in MrCAssetCache.WwwCache) 181 { 182 WWW download = keyValue.Value; 183 string m_bundleName = keyValue.Key; 184 185 // 下载失败 186 if (download.error != null) 187 { 188 MrCAssetCache.ErrorCache.Add(m_bundleName, download.error); 189 190 keysToRemove.Add(m_bundleName); 191 192 continue; 193 } 194 195 // 下载成功 196 if(download.isDone) 197 { 198 199 MrCAssetCache.BundleCache.Add(m_bundleName, new MrCAssetBundle(download.assetBundle,m_bundleName)); 200 201 keysToRemove.Add(m_bundleName); 202 } 203 } 204 205 // 删除下载成功的WWW对象 206 foreach( var key in keysToRemove) 207 { 208 WWW download = MrCAssetCache.WwwCache[key]; 209 210 MrCAssetCache.WwwCache.Remove(key); 211 212 download.Dispose(); 213 } 214 } 215 216 #endregion 217 } 218 }
加载包裹中的场景:
新建一个场景,创建TestScript脚本,放到摄像机上。
1 using UnityEngine; 2 using System.Collections; 3 //引入框架 4 using MrCAssetFramework; 5 6 public class TestScript : MonoBehaviour { 7 8 /// <summary> 9 /// 加载场景资源函数 10 /// </summary> 11 protected IEnumerator Load (string assetBundleName, string level) 12 { 13 IEnumerator b = da.LoadLevelAsync(assetBundleName, level, false); 14 yield return StartCoroutine(b); 15 Application.LoadLevel(level); 16 } 17 18 MrCAssetManager da; 19 void Start () 20 { 21 //指定统一资源标志符 22 MrCAssetManager.URI = "http://....../UnityFiles/AssetBundlesForBlog/"; 23 //主配置文件 24 MrCAssetManager.ManifestName = "Others"; 25 //加载成功后的操作代理 26 MrCAssetManager.InitBlock = ((bool bundleObj) => { 27 if (bundleObj) { 28 //协程操作加载的AssetBundle包裹 29 StartCoroutine (Load ("scenes/loaderscene.unity3d", "LoaderScene")); 30 } 31 }); 32 //开始我们的一切 33 da = MrCAssetManager.DefaultAssetManager; 34 } 35 }
加载包裹中的游戏对象等资源到场景中:
在上面的测试脚本中,修改部分代码。
1 /// <summary> 2 /// 加载游戏对象资源函数 3 /// </summary> 4 protected IEnumerator LoadObj(string assetBundleName, string obj) 5 { 6 IEnumerator b = da.LoadAssetAsync(assetBundleName, obj, typeof(GameObject)); 7 yield return StartCoroutine(b); 8 GameObject go = da.GetAsset<GameObject>(assetBundleName, obj); 9 GameObject.Instantiate(go); 10 } 11 //------------------------------------------------------------------------------- 12 //加载成功后的操作代理 13 MrCAssetManager.InitBlock = ((bool bundleObj) => { 14 if (bundleObj) { 15 //协程操作加载的游戏对象包裹 16 StartCoroutine(LoadObj("prefabs/cube.prefab", "MyCube")); 17 } 18 });