当前位置:   article > 正文

unity Assetbundle资源管理与更新比对下载_assetbundle如何替换更新

assetbundle如何替换更新

在做项目时为了减少包体的大小,我们可以用unity自带的AssetBundle进行资源打包管理,本篇博客采用的方案是在资源打包时给每个资源一个特定的MD5值,写入文本进行保存资源名称和对应的MD5值,并把资源和存储的文本放入服务器中;然后本地运行项目时首先判断本地是否有已下载好的资源和文本,然后把本地的文本与服务器的文本进行比对,如果资源的MD5值不对应那么就下载此资源到本地进行替换同时更新相应的资源名和对应的MD5值;如果本地没有此资源也要重新下载资源到本地并把资源名和对应的MD5值下载到本地进行保存。

每次项目启动时首先进行资源比对看是否需要更新下载。
1.把已带有bundle名的资源build出来,打包资源的脚本放在Editor文件夹下: 

  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEditor;
  4. using System.IO;
  5. using System.Text;
  6. using System;
  7. /// <summary>
  8. /// 把带有bundle名的资源build出来
  9. /// </summary>
  10. public class BuildAssetBundle : EditorWindow
  11. {
  12. /// <summary>
  13. /// 获取不同平台下的包裹存储路径
  14. /// </summary>
  15. public static string GetAssetBundlePath(BuildTarget target)
  16. {
  17. var path = PathConfig.buildAssetPath + "/" + PathConfig.GetBuildTargetPath(target) + "/";
  18. //当在硬盘目录结构里不存在该路径时,创建文件夹
  19. if (!Directory.Exists(path))
  20. {
  21. Directory.CreateDirectory(path);
  22. }
  23. return path;
  24. }
  25. [MenuItem("MrCAssetBundles/Build Windows")]
  26. public static void CustomBuildAssetBundle_Win()
  27. {
  28. BuildBundle(BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
  29. }
  30. [MenuItem("MrCAssetBundles/Build IOS")]
  31. public static void CustomBuildAssetBundle_IOS()
  32. {
  33. BuildBundle(BuildAssetBundleOptions.None, BuildTarget.iOS);
  34. }
  35. [MenuItem("MrCAssetBundles/Build MAC")]
  36. public static void CustomBuildAssetBundle_MAC()
  37. {
  38. BuildBundle(BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX);
  39. }
  40. [MenuItem("MrCAssetBundles/Build Android")]
  41. public static void CustomBuildAssetBundle_Android()
  42. {
  43. BuildBundle(BuildAssetBundleOptions.None, BuildTarget.Android);
  44. }
  45. [MenuItem("MrCAssetBundles/Build WebGL")]
  46. public static void CustomBuildAssetBundle_WebGL()
  47. {
  48. BuildBundle(BuildAssetBundleOptions.None, BuildTarget.WebGL);
  49. }
  50. private static void BuildBundle(BuildAssetBundleOptions bundleOptions, BuildTarget buildTarget)
  51. {
  52. //设置资源读取版本号
  53. //包裹存储的路径...
  54. string outputPath = GetAssetBundlePath(EditorUserBuildSettings.activeBuildTarget);
  55. if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath);
  56. //打包过程..
  57. BuildPipeline.BuildAssetBundles(outputPath, bundleOptions, buildTarget);
  58. CreateVersion(outputPath);
  59. Debug.Log("打包完成!位置: " + outputPath);
  60. }
  61. #region MD5版本号
  62. public static void CreateVersion(string resPath)
  63. {
  64. // 获取Res文件夹下所有文件的相对路径和MD5值
  65. string[] files = Directory.GetFiles(resPath, "*", SearchOption.AllDirectories);
  66. StringBuilder versions = new StringBuilder();
  67. for (int i = 0, len = files.Length; i < len; i++)
  68. {
  69. string filePath = files[i];
  70. if (filePath.Contains("."))
  71. {
  72. string extension = filePath.Substring(files[i].LastIndexOf("."));
  73. //只给后缀名为unity3d的文件生成MD5文件
  74. if (extension == ".unity3d")
  75. {
  76. string relativePath = filePath.Replace(resPath, "").Replace("\\", "/");
  77. string md5 = PathConfig.MD5File(filePath);
  78. versions.Append(relativePath).Append(",").Append(md5).Append("\r\n");
  79. }
  80. }
  81. else
  82. {
  83. string test = filePath.Substring(files[i].LastIndexOf("/") + 1);
  84. if (test == PathConfig.GetBuildTargetPath(EditorUserBuildSettings.activeBuildTarget))
  85. {
  86. string relativePath = filePath.Replace(resPath, "").Replace("\\", "/");
  87. string md5 = PathConfig.MD5File(filePath);
  88. versions.Append(relativePath).Append(",").Append(md5).Append("\r\n");
  89. }
  90. }
  91. }
  92. // 生成配置文件
  93. FileStream stream = new FileStream(resPath + PathConfig.version_file, FileMode.Create);
  94. byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
  95. stream.Write(data, 0, data.Length);
  96. stream.Flush();
  97. stream.Close();
  98. }
  99. #endregion
  100. }

2.资源build完成之后,后续更新比对,下载资源进行资源的加载

资源的路径管理脚本PathConfig:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. #if UNITY_EDITOR
  5. using UnityEditor;
  6. #endif
  7. /// <summary>
  8. /// 资源的路径管理脚本
  9. /// </summary>
  10. public class PathConfig
  11. {
  12. //最大build加载数量
  13. public static readonly int MAXBUNDLECOUNT = 5;
  14. public static readonly string bundleSuffix = ".unity3d";
  15. //保存打包的资源名与对应的MD5值
  16. public static readonly string version_file = "version.txt";
  17. public static readonly string localUrl = Application.persistentDataPath;
  18. //服务器下载资源地址
  19. public static readonly string serverUrl = @"file:///D:/PHP/Apache2.2/htdocs/LuaScript/";
  20. public static readonly string buildAssetPath = Application.streamingAssetsPath;
  21. //当前程序版本号(默认从1.0算起)
  22. public static string ProductVersion = "Asset_1.0";
  23. public static string GetFileHeader
  24. {
  25. get
  26. {
  27. #if UNITY_EDITOR
  28. return "file:///";
  29. #elif UNITY_IOS
  30. return "";
  31. #elif UNITY_STANDALONE_OSX
  32. return "";
  33. #elif UNITY_ANDROID
  34. return "jar:file://";
  35. #elif UNITY_WEBGL
  36. return "";
  37. #else
  38. return "file:///";
  39. #endif
  40. }
  41. }
  42. public static string GetManifestFileName()
  43. {
  44. var version = ProductVersion;
  45. #if UNITY_EDITOR
  46. return "Others/" + version;
  47. #elif UNITY_IOS
  48. return "IOS/"+ version;
  49. #elif UNITY_STANDALONE_OSX
  50. return "MacOS/"+ version;
  51. #elif UNITY_ANDROID
  52. return "Android/"+ version;
  53. #elif UNITY_WEBGL
  54. return "WebGL/"+ version;
  55. #else
  56. return "Others/"+ version;
  57. #endif
  58. }
  59. #if UNITY_EDITOR
  60. public static string GetBuildTargetPath(BuildTarget buildTarget)
  61. {
  62. var version = ProductVersion;
  63. switch (buildTarget)
  64. {
  65. case BuildTarget.iOS:
  66. return "IOS/" + version;
  67. case BuildTarget.StandaloneOSX:
  68. return "MacOS/" + version;
  69. case BuildTarget.Android:
  70. return "Android/" + version;
  71. case BuildTarget.WebGL:
  72. return "WebGL/" + version;
  73. default:
  74. return "Others/" + version;
  75. }
  76. }
  77. #endif
  78. //检查url,如果末尾有'/'不处理,无添加
  79. public static string CheckUrl(string url)
  80. {
  81. return url.Replace('\\', '/').TrimEnd('/') + '/';
  82. }
  83. //生成MD5值
  84. public static string MD5File(string file)
  85. {
  86. try
  87. {
  88. return MD5Checker.Check(file);
  89. }
  90. catch (System.Exception ex)
  91. {
  92. Debug.Log(ex.Message);
  93. return string.Empty;
  94. }
  95. }
  96. }

生成MD5管理MD5类:

  1. using System;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. /// <summary>
  5. /// 生成MD5管理MD5类
  6. /// </summary>
  7. public class MD5Checker
  8. {
  9. public delegate void AsyncCheckHeadler(AsyncCheckEventArgs e);
  10. public event AsyncCheckHeadler AsyncCheckProgress;
  11. //支持所有哈希算法
  12. private HashAlgorithm hashAlgorithm;
  13. //文件读取流
  14. private Stream inputStream;
  15. //缓存
  16. private byte[] asyncBuffer;
  17. public AsyncCheckState CompleteState { get; private set; }
  18. public float Progress { get; private set; }
  19. public string GetMD5 { get; private set; }
  20. /// <summary>
  21. /// 返回指定文件的MD5值
  22. /// </summary>
  23. /// <param name="path"></param>
  24. /// <returns></returns>
  25. public static string Check(string path)
  26. {
  27. try
  28. {
  29. var fs = new FileStream(path, FileMode.Open);
  30. MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
  31. byte[] buffer = md5Provider.ComputeHash(fs);
  32. string resule = BitConverter.ToString(buffer);
  33. resule = resule.Replace("-", "");
  34. fs.Close();
  35. return resule;
  36. }
  37. catch (ArgumentException aex)
  38. {
  39. throw new ArgumentException(string.Format("<{0}>, 不存在: {1}", path, aex.Message));
  40. }
  41. catch (Exception ex)
  42. {
  43. throw new Exception(string.Format("读取文件 {0} ,MD5失败: {1}", path, ex.Message));
  44. }
  45. }
  46. public static string Check_Stream(string path)
  47. {
  48. try
  49. {
  50. int bufferSize = 1024 * 256;//自定义缓冲区大小256K
  51. var buffer = new byte[bufferSize];
  52. Stream inputStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
  53. HashAlgorithm hashAlgorithm = new MD5CryptoServiceProvider();
  54. int readLength = 0;//每次读取长度
  55. var output = new byte[bufferSize];
  56. while ((readLength = inputStream.Read(buffer, 0, buffer.Length)) > 0)
  57. {
  58. //计算MD5
  59. hashAlgorithm.TransformBlock(buffer, 0, readLength, output, 0);
  60. }
  61. //完成最后计算,必须调用(由于上一部循环已经完成所有运算,所以调用此方法时后面的两个参数都为0)
  62. hashAlgorithm.TransformFinalBlock(buffer, 0, 0);
  63. string md5 = BitConverter.ToString(hashAlgorithm.Hash);
  64. hashAlgorithm.Clear();
  65. inputStream.Close();
  66. md5 = md5.Replace("-", "");
  67. return md5;
  68. }
  69. catch (ArgumentException aex)
  70. {
  71. throw new ArgumentException(string.Format("<{0}>, 不存在: {1}", path, aex.Message));
  72. }
  73. catch (Exception ex)
  74. {
  75. throw new Exception(string.Format("读取文件 {0} ,MD5失败: {1}", path, ex.Message));
  76. }
  77. }
  78. public void AsyncCheck(string path)
  79. {
  80. CompleteState = AsyncCheckState.Checking;
  81. try
  82. {
  83. int bufferSize = 1024 * 256;//缓冲区大小,1MB 1048576
  84. asyncBuffer = new byte[bufferSize];
  85. //打开文件流
  86. inputStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, bufferSize, true);
  87. hashAlgorithm = new MD5CryptoServiceProvider();
  88. //异步读取数据到缓冲区
  89. inputStream.BeginRead(asyncBuffer, 0, asyncBuffer.Length, new AsyncCallback(AsyncComputeHashCallback), null);
  90. }
  91. catch (ArgumentException aex)
  92. {
  93. throw new ArgumentException(string.Format("<{0}>, 不存在: {1}", path, aex.Message));
  94. }
  95. catch (Exception ex)
  96. {
  97. throw new Exception(string.Format("读取文件{0} ,MD5失败: {1}", path, ex.Message));
  98. }
  99. }
  100. private void AsyncComputeHashCallback(IAsyncResult result)
  101. {
  102. int bytesRead = inputStream.EndRead(result);
  103. //检查是否到达流末尾
  104. if (inputStream.Position < inputStream.Length)
  105. {
  106. //输出进度
  107. Progress = (float)inputStream.Position / inputStream.Length;
  108. string pro = string.Format("{0:P0}", Progress);
  109. if (null != AsyncCheckProgress)
  110. AsyncCheckProgress(new AsyncCheckEventArgs(AsyncCheckState.Checking, pro));
  111. var output = new byte[asyncBuffer.Length];
  112. //分块计算哈希值
  113. hashAlgorithm.TransformBlock(asyncBuffer, 0, asyncBuffer.Length, output, 0);
  114. //异步读取下一分块
  115. inputStream.BeginRead(asyncBuffer, 0, asyncBuffer.Length, new AsyncCallback(AsyncComputeHashCallback), null);
  116. return;
  117. }
  118. else
  119. {
  120. //计算最后分块哈希值
  121. hashAlgorithm.TransformFinalBlock(asyncBuffer, 0, bytesRead);
  122. }
  123. Progress = 1;
  124. string md5 = BitConverter.ToString(hashAlgorithm.Hash).Replace("-", "");
  125. CompleteState = AsyncCheckState.Completed;
  126. GetMD5 = md5;
  127. if (null != AsyncCheckProgress)
  128. AsyncCheckProgress(new AsyncCheckEventArgs(AsyncCheckState.Completed, GetMD5));
  129. inputStream.Close();
  130. }
  131. }
  132. public enum AsyncCheckState
  133. {
  134. Completed,
  135. Checking
  136. }
  137. public class AsyncCheckEventArgs : EventArgs
  138. {
  139. public string Value { get; private set; }
  140. public AsyncCheckState State { get; private set; }
  141. public AsyncCheckEventArgs(AsyncCheckState state, string value)
  142. {
  143. this.Value = value; this.State = state;
  144. }
  145. }

3.Assetbundle自我管理的类:

MrCAssetBundle类管理bundle自身

  1. using UnityEngine;
  2. /// <summary>
  3. /// MrCAssetBundle类管理bundle自身
  4. /// </summary>
  5. public sealed class MrCAssetBundle
  6. {
  7. internal string m_AssetBundleName;
  8. public AssetBundle AssetBundle { get; set; }
  9. public float Progress { get; set; }
  10. public bool IsDone { get; set; }
  11. internal MrCAssetBundle(string assetBundleName)
  12. {
  13. this.m_AssetBundleName = assetBundleName;
  14. this.m_ReferencedCount = 1;
  15. }
  16. private int m_ReferencedCount;
  17. //保留调用次数
  18. public void RetainCall()
  19. {
  20. this.m_ReferencedCount++;
  21. }
  22. //卸载资源
  23. public void Release()
  24. {
  25. //this.m_ReferencedCount--;
  26. 当引用计数为0时,卸载资源
  27. //if (this.m_ReferencedCount == 0)
  28. //{
  29. // if(AssetBundle!=null)
  30. // this.AssetBundle.Unload(true);
  31. // MrCAssetCache.FreeBundle(this.m_AssetBundleName);
  32. //}
  33. if (AssetBundle != null)
  34. this.AssetBundle.Unload(true);
  35. MrCAssetCache.FreeBundle(this.m_AssetBundleName);
  36. Debug.Log("卸载资源: " + m_AssetBundleName);
  37. }
  38. //如果是新创建的,不首先消毁
  39. public int RetainCount()
  40. {
  41. var newbundle = MrCAssetManager.Instance.NewAssetBundle;
  42. if (newbundle != null && newbundle == this)
  43. {
  44. return 65535;
  45. }
  46. return m_ReferencedCount;
  47. }
  48. }

MrCAssetCache类缓存bundle:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /// <summary>
  5. /// MrCAssetCache类缓存bundle
  6. /// </summary>
  7. internal sealed class MrCAssetCache //缓存管理
  8. {
  9. #region 包裹缓存机制
  10. //创建缓存字典
  11. private static Dictionary<string, MrCAssetBundle> assetBundleCache;
  12. //缓存字典属性
  13. internal static Dictionary<string, MrCAssetBundle> AssetBundleCache
  14. {
  15. get
  16. {
  17. if (assetBundleCache == null)
  18. {
  19. assetBundleCache = new Dictionary<string, MrCAssetBundle>();
  20. }
  21. return assetBundleCache;
  22. }
  23. }
  24. //创建缓存WWW对象
  25. private static Dictionary<string, AssetBundleCreateRequest> wwwCache;
  26. //创建缓存WWW对象属性
  27. private static Dictionary<string, AssetBundleCreateRequest> WwwCache
  28. {
  29. get
  30. {
  31. if (wwwCache == null)
  32. {
  33. wwwCache = new Dictionary<string, AssetBundleCreateRequest>();
  34. }
  35. return wwwCache;
  36. }
  37. }
  38. //创建依赖缓存对象
  39. private static Dictionary<string, string[]> dependCache;
  40. //创建依赖缓存属性
  41. private static Dictionary<string, string[]> DependCache
  42. {
  43. get
  44. {
  45. if (dependCache == null)
  46. {
  47. dependCache = new Dictionary<string, string[]>();
  48. }
  49. return dependCache;
  50. }
  51. }
  52. /// <summary>
  53. /// Instantiate the Cache
  54. /// </summary>
  55. /// <param name="assetbundleName"></param>
  56. /// <returns></returns>
  57. internal static bool InCache(string assetbundleName)
  58. {
  59. return AssetBundleCache.ContainsKey(assetbundleName);
  60. }
  61. #endregion
  62. #region 卸载系列函数
  63. /// <summary>
  64. /// 卸载资源包和依赖包
  65. /// </summary>
  66. /// <param name="assetBundleName"></param>
  67. public static void UnloadAssetBundle(string assetBundleName)
  68. {
  69. UnloadAssetBundleInternal(assetBundleName);
  70. UnloadDependencies(assetBundleName);
  71. }
  72. internal static void UnloadDependencies(string assetBundleName)
  73. {
  74. string[] depends = null;
  75. //获取所有的依赖包名称
  76. if (!DependCache.TryGetValue(assetBundleName, out depends))
  77. return;
  78. //卸载依赖包
  79. foreach (var dependency in depends)
  80. {
  81. UnloadAssetBundleInternal(dependency);
  82. }
  83. //删除依赖缓存策略
  84. DependCache.Remove(assetBundleName);
  85. }
  86. internal static void UnloadAssetBundleInternal(string assetBundleName)
  87. {
  88. MrCAssetBundle bundle;
  89. AssetBundleCache.TryGetValue(assetBundleName, out bundle);
  90. if (bundle == null)
  91. return;
  92. bundle.Release();
  93. }
  94. #endregion
  95. #region GetFunction
  96. internal static AssetBundleCreateRequest GetWWWCache(string key)
  97. {
  98. if (WwwCache.ContainsKey(key))
  99. {
  100. return WwwCache[key];
  101. }
  102. return null;
  103. }
  104. internal static MrCAssetBundle SetWWWCache(string key, AssetBundleCreateRequest value)
  105. {
  106. if (!WwwCache.ContainsKey(key))
  107. WwwCache.Add(key, value);
  108. else
  109. WwwCache[key] = value;
  110. var bundleObject = new MrCAssetBundle(key);
  111. SetBundleCache(key, bundleObject);
  112. return bundleObject;
  113. }
  114. internal static MrCAssetBundle GetBundleCache(string key)
  115. {
  116. MrCAssetBundle ab;
  117. AssetBundleCache.TryGetValue(key, out ab);
  118. return ab;
  119. }
  120. internal static void SetBundleCache(string key, MrCAssetBundle value)
  121. {
  122. if (!AssetBundleCache.ContainsKey(key))
  123. {
  124. AssetBundleCache.Add(key, value);
  125. }
  126. else
  127. {
  128. AssetBundleCache[key] = value;
  129. }
  130. }
  131. internal static string[] GetDependCache(string key)
  132. {
  133. string[] depends;
  134. DependCache.TryGetValue(key, out depends);
  135. return depends;
  136. }
  137. internal static void SetDependCache(string key, string[] value)
  138. {
  139. if (!DependCache.ContainsKey(key))
  140. {
  141. DependCache.Add(key, value);
  142. }
  143. }
  144. #endregion
  145. internal static void FreeBundle(string key)
  146. {
  147. if (AssetBundleCache.ContainsKey(key))
  148. AssetBundleCache.Remove(key);
  149. }
  150. #region Update
  151. private static List<string> keysToRemove = new List<string>();
  152. internal static void Update()
  153. {
  154. foreach (var keyValue in WwwCache)
  155. {
  156. var download = keyValue.Value;
  157. string bundleName = keyValue.Key;
  158. var bundleObject = GetBundleCache(bundleName);
  159. if (bundleObject == null)
  160. {
  161. bundleObject = new MrCAssetBundle(bundleName);
  162. SetBundleCache(bundleName, bundleObject);
  163. }
  164. bundleObject.Progress = download.progress;
  165. //下载成功
  166. if (download.isDone)
  167. {
  168. bundleObject.AssetBundle = download.assetBundle;
  169. bundleObject.IsDone = true;
  170. bundleObject.Progress = 1.1f;
  171. keysToRemove.Add(bundleName);
  172. }
  173. }
  174. //删除下载成功的WWW对象
  175. foreach (var key in keysToRemove)
  176. {
  177. if (wwwCache.ContainsKey(key))
  178. {
  179. var download = WwwCache[key];
  180. WwwCache.Remove(key);
  181. }
  182. }
  183. keysToRemove.Clear();
  184. }
  185. #endregion
  186. }

MrCAssetManager类:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. //AssetBundle管理
  5. public class MrCAssetManager : MonoBehaviour
  6. {
  7. #region Singleton
  8. private static MrCAssetManager _instance = null;
  9. public static MrCAssetManager Instance
  10. {
  11. get
  12. {
  13. if (_instance == null)
  14. {
  15. var obj = new GameObject("LoadAssetManager");
  16. _instance = obj.AddComponent<MrCAssetManager>();
  17. }
  18. return _instance;
  19. }
  20. }
  21. #endregion
  22. //最新生成的bundle
  23. public MrCAssetBundle NewAssetBundle { get; set; }
  24. public void Init()
  25. {
  26. StartCoroutine(LoadManifestBundle());
  27. }
  28. // 异步加载场景
  29. public IEnumerator LoadLevelAsync(string assetBundleName, string assetName)
  30. {
  31. var bundleObject = MrCAssetCache.GetBundleCache(assetBundleName);
  32. //缓存中已经存在请求bundle,中止..
  33. if (bundleObject == null)
  34. {
  35. //通过网络下载AssetBundle
  36. bundleObject = LoadAssetBundleAtInternal(assetBundleName);
  37. yield return LoadAssetBundleProgress(bundleObject);
  38. //StartCoroutine(LoadDependencies(assetBundleName));
  39. }
  40. else
  41. {
  42. bundleObject.RetainCall();
  43. }
  44. }
  45. private void Update()
  46. {
  47. MrCAssetCache.Update();
  48. }
  49. public IEnumerator LoadManifestBundle()
  50. {
  51. var manifestName = PathConfig.GetManifestFileName();
  52. var bundleObject = MrCAssetCache.GetBundleCache(manifestName);
  53. //缓存中已经存在请求bundle,中止..
  54. if (bundleObject == null)
  55. {
  56. //通过网络下载AssetBundle
  57. bundleObject = LoadAssetBundleAtInternal(manifestName);
  58. yield return LoadAssetBundleProgress(bundleObject);
  59. }
  60. else
  61. {
  62. bundleObject.RetainCall();
  63. }
  64. }
  65. #region 加载包裹系列函数
  66. ///检查是否已经从网络下载
  67. protected MrCAssetBundle LoadAssetBundleAtInternal(string assetBundleName)
  68. {
  69. var bundleObject = MrCAssetCache.GetBundleCache(assetBundleName);
  70. //如果WWW缓存策略中包含有对应的关键字,则返回true
  71. if (bundleObject == null)
  72. {
  73. MustBundleHandle();
  74. var url = PathConfig.localUrl + "/"
  75. + PathConfig.GetManifestFileName() + "/"
  76. + assetBundleName;
  77. //创建下载链接
  78. var request = AssetBundle.LoadFromFileAsync(url);
  79. //加入缓存策略
  80. NewAssetBundle = MrCAssetCache.SetWWWCache(assetBundleName, request);
  81. return NewAssetBundle;
  82. }
  83. return bundleObject;
  84. }
  85. //超过最大bunld数处理
  86. private void MustBundleHandle()
  87. {
  88. var bundles = MrCAssetCache.AssetBundleCache;
  89. if (bundles != null && bundles.Count >= PathConfig.MAXBUNDLECOUNT)
  90. {
  91. int min = int.MaxValue;
  92. string findKey = string.Empty;
  93. foreach (var item in bundles.Values)
  94. {
  95. if (item.RetainCount() < min)
  96. {
  97. min = item.RetainCount();
  98. findKey = item.m_AssetBundleName;
  99. }
  100. }
  101. var bundle = MrCAssetCache.GetBundleCache(findKey);
  102. if (bundle != null) bundle.Release();
  103. }
  104. }
  105. public IEnumerator LoadDependencies(string assetBundleName)
  106. {
  107. var manifestName = PathConfig.GetManifestFileName();
  108. var manifest = this.GetAsset<AssetBundleManifest>(manifestName, "AssetBundleManifest");
  109. if (manifest == null)
  110. {
  111. if (MrCAssetCache.InCache(manifestName))
  112. {
  113. manifest = this.GetAsset<AssetBundleManifest>(manifestName, "AssetBundleManifest");
  114. }
  115. else
  116. yield return null;
  117. }
  118. //获取依赖包裹
  119. string[] depends = manifest.GetAllDependencies(assetBundleName);
  120. if (depends.Length == 0)
  121. {
  122. yield return null;
  123. }
  124. //记录并且加载所有的依赖包裹
  125. MrCAssetCache.SetDependCache(assetBundleName, depends);
  126. for (int i = 0; i < depends.Length; i++)
  127. {
  128. yield return LoadAssetBundleAtInternal(depends[i]);
  129. }
  130. }
  131. /// <summary>
  132. /// 用于加载资源
  133. /// </summary>
  134. /// <typeparam name="T"></typeparam>
  135. /// <param name="assetbundleName"></param>
  136. /// <param name="assetName"></param>
  137. /// <returns></returns>
  138. public T GetAsset<T>(string assetbundleName, string assetName) where T : UnityEngine.Object
  139. {
  140. MrCAssetBundle lab = MrCAssetCache.GetBundleCache(assetbundleName);
  141. if (lab != null)
  142. {
  143. return lab.AssetBundle.LoadAsset<T>(assetName);
  144. }
  145. else
  146. {
  147. print("资源: " + assetbundleName + " 未加载或正在加载!");
  148. return null;
  149. }
  150. }
  151. #endregion
  152. #region Loading阶段
  153. private IEnumerator LoadAssetBundleProgress(MrCAssetBundle _bundleObject)
  154. {
  155. if (_bundleObject == null)
  156. {
  157. yield break;
  158. }
  159. //当前进度
  160. //int initNum = 0;
  161. //目标进度
  162. //int maxNum = 2000;
  163. while (!_bundleObject.IsDone)
  164. {
  165. yield return new WaitForEndOfFrame();
  166. }
  167. }
  168. #endregion
  169. }

UpdateAssets类:负责比对更新下载

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using UnityEngine;
  8. using UnityEngine.Networking;
  9. public class UpdateAssets : MonoBehaviour
  10. {
  11. //保存本地的assetbundle名和对应的MD5值
  12. private Dictionary<string, string> LocalResVersion;
  13. private Dictionary<string, string> ServerResVersion;
  14. //保存需要更新的AssetBundle名
  15. private List<string> NeedDownFiles;
  16. //是否需要下载文件
  17. private bool NeedUpdateLocalVersionFile = false;
  18. private string _localUrl;
  19. private string _serverUrl;
  20. public IEnumerator OnStart()
  21. {
  22. yield return Init();
  23. }
  24. private IEnumerator Init()
  25. {
  26. LocalResVersion = new Dictionary<string, string>();
  27. ServerResVersion = new Dictionary<string, string>();
  28. NeedDownFiles = new List<string>();
  29. //加载本地version配置
  30. _localUrl = PathConfig.localUrl + "/" + PathConfig.GetManifestFileName() + "/";
  31. Debug.Log(PathConfig.GetFileHeader + _localUrl);
  32. yield return DownLoad(PathConfig.GetFileHeader + _localUrl, PathConfig.version_file, LocalVersionCallBack);
  33. //加载服务端version配置
  34. var serverUrl = PathConfig.serverUrl;
  35. _serverUrl = serverUrl + PathConfig.GetManifestFileName() + "/";
  36. Debug.Log("下载文件服务器: " + _serverUrl);
  37. yield return DownLoad(_serverUrl, PathConfig.version_file, ServerVersionCallBack);
  38. }
  39. /// <summary>
  40. /// 下载本地版本的回调
  41. /// </summary>
  42. /// <param name="request"></param>
  43. /// <param name="param"></param>
  44. /// <returns></returns>
  45. private IEnumerator LocalVersionCallBack(UnityWebRequest request, string param = "")
  46. {
  47. Debug.Log("本地下载");
  48. //保存本地的version
  49. var context = request.downloadHandler.text;
  50. ParseVersionFile(context, LocalResVersion);
  51. yield return ClearIncompleteFile();
  52. }
  53. /// <summary>
  54. /// 下载服务器版本的回调
  55. /// </summary>
  56. /// <param name="request"></param>
  57. /// <param name="param"></param>
  58. /// <returns></returns>
  59. private IEnumerator ServerVersionCallBack(UnityWebRequest request, string param = "")
  60. {
  61. Debug.Log("服务器下载");
  62. //保存服务端version
  63. var context = request.downloadHandler.text;
  64. ParseVersionFile(context, ServerResVersion);
  65. //根据用户名过滤下载资源
  66. //FilterServerDownLoadFile();
  67. //计算出需要重新加载的资源
  68. CompareVersion();
  69. if (NeedUpdateLocalVersionFile)
  70. {
  71. //DownLoadProgress.Instance.ShowDownLoad(NeedDownFiles.Count);
  72. Debug.Log("需要更新的资源个数为:" + NeedDownFiles.Count);
  73. }
  74. //加载需要更新的资源
  75. yield return DownLoadRes();
  76. }
  77. //对比本地配置,清除缺失文件
  78. private IEnumerator ClearIncompleteFile()
  79. {
  80. if (LocalResVersion != null)
  81. {
  82. List<string> removeKey = new List<string>();
  83. foreach (var local in LocalResVersion)
  84. {
  85. string filePath = _localUrl + local.Key;
  86. if (!Directory.Exists(_localUrl))
  87. {
  88. Directory.CreateDirectory(_localUrl);
  89. }
  90. if (!File.Exists(filePath))
  91. {
  92. removeKey.Add(local.Key);
  93. }
  94. else
  95. {
  96. //异步
  97. yield return MD5FileAsync(filePath, delegate (string md5)
  98. {
  99. if (md5 != local.Value)
  100. {
  101. File.Delete(filePath);
  102. removeKey.Add(local.Key);
  103. }
  104. });
  105. }
  106. }
  107. foreach (var key in removeKey)
  108. {
  109. if (LocalResVersion.ContainsKey(key))
  110. LocalResVersion.Remove(key);
  111. }
  112. }
  113. }
  114. //依次加载需要更新的资源
  115. private IEnumerator DownLoadRes()
  116. {
  117. if (NeedDownFiles.Count == 0)
  118. {
  119. UpdateLocalVersionFile();
  120. yield break;
  121. }
  122. string file = NeedDownFiles[0];
  123. NeedDownFiles.RemoveAt(0);
  124. yield return DownLoad(_serverUrl, file, DownLoadCallBack);
  125. }
  126. /// <summary>
  127. /// 下载需要更新的文件
  128. /// </summary>
  129. /// <param name="request"></param>
  130. /// <param name="param"></param>
  131. /// <returns></returns>
  132. private IEnumerator DownLoadCallBack(UnityWebRequest request, string param = "")
  133. {
  134. Debug.Log("下载需要更新的资源");
  135. //将下载的资源替换本地就的资源
  136. var download = request.downloadHandler;
  137. if (!request.isNetworkError && !request.isHttpError)
  138. {
  139. ReplaceLocalRes(param, download.data);
  140. if (ServerResVersion.ContainsKey(param))
  141. {
  142. if (LocalResVersion.ContainsKey(param))
  143. LocalResVersion[param] = ServerResVersion[param];
  144. else
  145. LocalResVersion.Add(param, ServerResVersion[param]);
  146. }
  147. }
  148. yield return DownLoadRes();
  149. }
  150. /// <summary>
  151. /// 替换本地的资源文件
  152. /// </summary>
  153. /// <param name="fileName"></param>
  154. /// <param name="data"></param>
  155. private void ReplaceLocalRes(string fileName, byte[] data)
  156. {
  157. string filePath = _localUrl + fileName;
  158. if (!Directory.Exists(_localUrl))
  159. {
  160. Directory.CreateDirectory(_localUrl);
  161. }
  162. FileStream stream = new FileStream(filePath, FileMode.Create);
  163. stream.Write(data, 0, data.Length);
  164. stream.Flush();
  165. stream.Close();
  166. }
  167. //更新本地的version配置
  168. private void UpdateLocalVersionFile()
  169. {
  170. if (NeedUpdateLocalVersionFile)
  171. {
  172. StringBuilder versions = new StringBuilder();
  173. foreach (var item in LocalResVersion)
  174. {
  175. versions.Append(item.Key).Append(",").Append(item.Value).Append("\r\n");
  176. }
  177. if (!Directory.Exists(_localUrl))
  178. {
  179. Directory.CreateDirectory(_localUrl);
  180. }
  181. FileStream stream = new FileStream(_localUrl + PathConfig.version_file, FileMode.Create);
  182. byte[] data = Encoding.UTF8.GetBytes(versions.ToString());
  183. stream.Write(data, 0, data.Length);
  184. stream.Flush();
  185. stream.Close();
  186. }
  187. //加载显示对象
  188. //StartCoroutine(Show());
  189. }
  190. /// <summary>
  191. /// 计算出需要更新的文件
  192. /// </summary>
  193. private void CompareVersion()
  194. {
  195. foreach (var version in ServerResVersion)
  196. {
  197. string fileName = version.Key; //assetbundleName
  198. string serverMd5 = version.Value; // asset MD5值
  199. //新增的资源
  200. if (!LocalResVersion.ContainsKey(fileName))
  201. {
  202. NeedDownFiles.Add(fileName);
  203. }
  204. else
  205. {
  206. //需要替换的资源
  207. string localMd5;
  208. LocalResVersion.TryGetValue(fileName, out localMd5);
  209. if (!serverMd5.Equals(localMd5))
  210. {
  211. NeedDownFiles.Add(fileName);
  212. }
  213. }
  214. }
  215. if (NeedDownFiles.Count > 0)
  216. {
  217. for (int i = 0; i < NeedDownFiles.Count; i++)
  218. {
  219. Debug.Log("需要更新的资源:" + NeedDownFiles[i]);
  220. }
  221. }
  222. //本次有更新,同时更新本地的version.txt
  223. NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0;
  224. }
  225. /// <summary>
  226. /// 解析版本文件
  227. /// </summary>
  228. /// <param name="content"></param>
  229. /// <param name="dict"></param>
  230. private void ParseVersionFile(string content, Dictionary<string, string> dict)
  231. {
  232. if (content == null || content.Length == 0)
  233. {
  234. return;
  235. }
  236. string[] items = content.Split('\n');
  237. foreach (string item in items)
  238. {
  239. string str = item.Replace("\r", "").Replace("\n", "").Replace(" ", "");
  240. string[] info = str.Split(',');
  241. if (info != null && info.Length == 2)
  242. {
  243. dict.Add(info[0], info[1]);
  244. }
  245. }
  246. }
  247. /// <summary>
  248. /// 下载文件
  249. /// </summary>
  250. /// <param name="url"></param>
  251. /// <param name="fileName"></param>
  252. /// <param name="finishFun"></param>
  253. /// <returns></returns>
  254. private IEnumerator DownLoad(string url, string fileName, HandleFinishDownload finishFun)
  255. {
  256. url = PathConfig.CheckUrl(url);
  257. var request = UnityWebRequest.Get(url + fileName);
  258. if (NeedUpdateLocalVersionFile)
  259. {
  260. yield return LoadRegularRequest(request);
  261. }
  262. else
  263. {
  264. yield return request.SendWebRequest();
  265. }
  266. if (finishFun != null && request.isDone)
  267. {
  268. yield return finishFun(request, fileName);
  269. }
  270. if (request.isNetworkError)
  271. {
  272. Debug.LogError("更新资源出错: " + url + " error: " + request.error);
  273. }
  274. request.Dispose();
  275. }
  276. public delegate IEnumerator HandleFinishDownload(UnityWebRequest request, string param = "");
  277. //异步生成MD5值
  278. private IEnumerator MD5FileAsync(string file, Action<string> action)
  279. {
  280. var asyncChecker = new MD5Checker();
  281. asyncChecker.AsyncCheck(file);
  282. var endframe = new WaitForEndOfFrame();
  283. while (asyncChecker.CompleteState == AsyncCheckState.Checking)
  284. {
  285. //SeerLogger.Log("load...{0:P0}" + asyncChecker.Progress);
  286. yield return endframe;
  287. }
  288. action(asyncChecker.GetMD5);
  289. }
  290. //整齐的下载资源
  291. public IEnumerator LoadRegularRequest(UnityEngine.Networking.UnityWebRequest request)
  292. {
  293. var ao = request.SendWebRequest();
  294. bool downError = false;
  295. //ao.allowSceneActivation = false;
  296. while (true)
  297. {
  298. if (downError) break;
  299. if (ao.webRequest.isNetworkError || ao.webRequest.isHttpError)
  300. {
  301. downError = true;
  302. }
  303. else if (ao.isDone)
  304. break;
  305. }
  306. yield return new WaitForEndOfFrame();
  307. }
  308. //ao.allowSceneActivation = true;
  309. }

最后是我们自己的测试登陆:TestManager类:

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using XLua;
  6. public class TestManager : MonoBehaviour
  7. {
  8. private static TestManager _instance;
  9. public UpdateAssets updateAssets;
  10. private void Awake()
  11. {
  12. if (_instance == null)
  13. {
  14. _instance = this;
  15. }
  16. }
  17. public static TestManager Instance
  18. {
  19. get { return _instance; }
  20. }
  21. public bool IsBundleSuccess { get; set; }
  22. void Start()
  23. {
  24. StartCoroutine(InitUpdate(LoadGameObject));
  25. }
  26. private IEnumerator InitUpdate(Action action)
  27. {
  28. if (updateAssets)
  29. {
  30. yield return updateAssets.OnStart();
  31. }
  32. if (action != null)
  33. {
  34. action();
  35. }
  36. }
  37. /// <summary>
  38. /// 加载游戏对象
  39. /// </summary>
  40. public void LoadGameObject()
  41. {
  42. StartCoroutine(LoadGameObjectCoroutine("test1.unity3d", "Cube",null));
  43. }
  44. public IEnumerator LoadGameObjectCoroutine(string gameObjectName, string assetName, Action action)
  45. {
  46. yield return MrCAssetManager.Instance.LoadLevelAsync(gameObjectName, assetName);
  47. GameObject go = MrCAssetManager.Instance.GetAsset<GameObject>(gameObjectName, assetName);
  48. Instantiate(go);
  49. LuaEnv luaEnv = new LuaEnv();
  50. TextAsset txtAsset = MrCAssetManager.Instance.GetAsset<TextAsset>(gameObjectName,"1.lua");
  51. luaEnv.DoString(txtAsset.text);
  52. luaEnv.Dispose();
  53. }
  54. }

这里的测试类和大神的测试类有点不一样,不懂了再去看大神的。

以上就是assetbundle打包到比对更新,下载资源,解压应用的整套流程。浏览之前最好对unity  Assetbundle的打包流程到应用有所了解之后再结合应用到自己的项目中。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/92048
推荐阅读
相关标签
  

闽ICP备14008679号