HybridCLR+Addressables 华佗热更

[复制链接]
查看987 | 回复0 | 2024-11-25 19:26:28 | 显示全部楼层 |阅读模式
本项目工程是用2022.3.17f1c1,亲测可用,工程在附件链接里。
资源均来源于网络,仅供学习和参考使用,版权归原作者所有,勿作商业用途,请在下载后24小时之内自觉删除。
本站发布的内容若无意中侵犯到您的权益,请联系我们,本站将在看到后立即删除。
  1. 华佗热更aa包:在Unity中HybridCLR中,导入CLR5.2.0,包名:com.code-philosophy.hybridclr,链接:https://gitee.com/focus-creative-games/hybridclr_unity.git,在build中设置il2cpp,在HybridCLR中installer中安装对应版本,在setting中设置HotUpdate程序集,在Generate中点All生成所有,在CompileDll中ActiveBuildTarge激活,然后点击BuildTool复制dll到aa包中,导入Addressables,包名:com.unity.addressables,链接:github.com/Unity-Technologies/Addressables-Sample,在Addressables中设置Remote远端资源获取,开启Host中的服务,在Group中Build资源或者Update资源,那么资源将打在ServerData中,当生成exe后会从ServerData中读取更新资源,从而下载到沙盒缓存目录中。 Addressables资源加载时完成后释放资源句柄 应注释掉释放资源句柄,否则会导致材质丢失//Addressables.Release(handle);
复制代码
  1. using HybridCLR;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using UnityEngine;
  7. using UnityEngine.AddressableAssets;
  8. using UnityEngine.AddressableAssets.ResourceLocators;
  9. using UnityEngine.ResourceManagement.AsyncOperations;
  10. using UnityEngine.UI; // 使用UI命名空间

  11. public class CheckUpdateCtrl : MonoBehaviour
  12. {
  13. // 元数据文件列表,这些是AOT程序集的元数据文件
  14. private static readonly List<string> MetadataList = new List<string>()
  15. {
  16. "mscorlib.dll",
  17. "System.Core.dll",
  18. "System.dll",
  19. };

  20. [Serializable]
  21. private class DownloadInfo
  22. {
  23. public List<string> CatalogsInfo = new List<string>(); // 保存需要下载的目录信息
  24. }

  25. private DownloadInfo m_DownloadInfo = new DownloadInfo(); // 实例化一个DownloadInfo对象
  26. private List<object> DownloadCatalogsKey = new List<object>(); // 用于保存下载资源的Key列表
  27. private const string DOWNLOADKEY = "DOWNLOADKEY"; // 保存更新信息的PlayerPrefs键

  28. // 检查是否有目录更新
  29. private bool HasCatalogsUpdate => m_DownloadInfo.CatalogsInfo?.Count > 0;


  30. // 进度条相关
  31. public Slider progressBar; // 用于显示进度的滑动条
  32. public Text progressText; // 用于显示进度百分比的文本

  33. // MonoBehaviour的Start方法,在游戏开始时调用
  34. void Start()
  35. {
  36. DontDestroyOnLoad(this); // 保证该对象在场景切换时不被销毁
  37. StartCoroutine(Check()); // 启动更新检查的协程
  38. }

  39. private IEnumerator Check()
  40. {
  41. // 按顺序执行以下步骤
  42. yield return LoadMetadata(); // 加载元数据
  43. yield return CheckAssetUpdate(); // 检查资源更新
  44. yield return DownloadAsset(); // 下载更新的资源
  45. yield return LoadHotUpdateAssembly(); // 加载热更新的程序集
  46. yield return LoadCube(); // 生成Cube对象
  47. yield return LoadScene("EntryScene"); // 加载热更新的场景EntryScene
  48. yield return LoadScene("GameScene"); // 加载热更新的场景GameScene
  49. yield return LoadPlayer(); // 生成Player对象
  50. }

  51. private IEnumerator LoadMetadata()
  52. {
  53. foreach (var str in MetadataList)
  54. {
  55. // 异步加载元数据文件
  56. var handle = Addressables.LoadAssetAsync<TextAsset>(str);
  57. yield return handle;

  58. if (handle.Status == AsyncOperationStatus.Succeeded)
  59. {
  60. byte[] metadata = handle.Result.bytes; // 获取元数据字节数组
  61. // 加载元数据
  62. LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(metadata, HomologousImageMode.SuperSet);
  63. Debug.Log($"Loaded metadata for {str}, result: {err}");
  64. }
  65. else
  66. {
  67. Debug.LogError($"Failed to load metadata for {str}");
  68. }

  69. Addressables.Release(handle); // 释放资源句柄
  70. }
  71. }

  72. private IEnumerator CheckAssetUpdate()
  73. {
  74. var catalogsHandle = Addressables.CheckForCatalogUpdates(false); // 检查目录更新
  75. yield return catalogsHandle;

  76. if (catalogsHandle.Status == AsyncOperationStatus.Succeeded)
  77. {
  78. m_DownloadInfo.CatalogsInfo = catalogsHandle.Result; // 获取更新目录列表
  79. Addressables.Release(catalogsHandle); // 释放资源句柄

  80. if (HasCatalogsUpdate)
  81. {
  82. // 服务器有更新
  83. Debug.Log("Server has resource updates");
  84. string jsonStr = JsonUtility.ToJson(m_DownloadInfo);
  85. PlayerPrefs.SetString(DOWNLOADKEY, jsonStr); // 保存更新目录信息到本地
  86. }
  87. else if (PlayerPrefs.HasKey(DOWNLOADKEY))
  88. {
  89. // 本地有未完成的更新
  90. Debug.Log("Unfinished updates found in local storage");
  91. string savedStr = PlayerPrefs.GetString(DOWNLOADKEY);
  92. JsonUtility.FromJsonOverwrite(savedStr, m_DownloadInfo); // 读取本地保存的更新目录信息
  93. }

  94. if (HasCatalogsUpdate)
  95. {
  96. var locatorHandle = Addressables.UpdateCatalogs(m_DownloadInfo.CatalogsInfo, false); // 更新目录
  97. yield return locatorHandle;

  98. if (locatorHandle.Status == AsyncOperationStatus.Succeeded)
  99. {
  100. Debug.Log("Catalog update succeeded");
  101. List<IResourceLocator> resourceLocators = locatorHandle.Result; // 获取资源定位器列表
  102. Addressables.Release(locatorHandle); // 释放资源句柄

  103. DownloadCatalogsKey.Clear(); // 清空下载Key列表
  104. foreach (var locator in resourceLocators)
  105. {
  106. DownloadCatalogsKey.AddRange(locator.Keys); // 添加新的资源Key
  107. }
  108. }
  109. else
  110. {
  111. Debug.LogError("Catalog update failed");
  112. }
  113. }
  114. }
  115. else
  116. {
  117. Debug.LogError("Failed to check for catalog updates");
  118. }
  119. }

  120. private IEnumerator DownloadAsset()
  121. {
  122. // 等待一段时间来模拟资源加载速度
  123. yield return new WaitForSeconds(3.0f);

  124. var sizeHandle = Addressables.GetDownloadSizeAsync((IEnumerable)DownloadCatalogsKey); // 获取需要下载的资源大小
  125. yield return sizeHandle;

  126. if (sizeHandle.Status == AsyncOperationStatus.Succeeded)
  127. {
  128. long size = sizeHandle.Result; // 获取资源大小
  129. if (size == 0) yield break; // 如果没有需要下载的资源,则跳过

  130. Debug.Log("Downloading resources");
  131. var downloadHandle = Addressables.DownloadDependenciesAsync((IEnumerable)DownloadCatalogsKey, Addressables.MergeMode.Union, false); // 下载资源

  132. while (!downloadHandle.IsDone) // 在下载完成之前,更新进度条
  133. {
  134. float progress = downloadHandle.PercentComplete; // 获取下载进度
  135. UpdateProgressBar(progress); // 更新进度条
  136. yield return null;
  137. }

  138. if (downloadHandle.Status == AsyncOperationStatus.Succeeded)
  139. {
  140. PlayerPrefs.DeleteKey(DOWNLOADKEY); // 下载完成后删除本地保存的更新信息
  141. Debug.Log("Download completed");
  142. }
  143. else
  144. {
  145. Debug.LogError("Download failed");
  146. }

  147. Addressables.Release(downloadHandle); // 释放资源句柄
  148. }
  149. else
  150. {
  151. Debug.LogError("Failed to get download size");
  152. }

  153. Addressables.Release(sizeHandle); // 释放资源句柄
  154. }

  155. private void UpdateProgressBar(float progress)
  156. {
  157. if (progressBar != null)
  158. {
  159. progressBar.value = progress; // 更新进度条的值
  160. }

  161. if (progressText != null)
  162. {
  163. progressText.text = $"{progress * 100:F2}%"; // 更新进度百分比文本
  164. }
  165. }

  166. private IEnumerator LoadHotUpdateAssembly()
  167. {
  168. string label = "Assembly"; // 热更新程序集的标签
  169. var assembliesHandle = Addressables.LoadAssetsAsync<TextAsset>(label, null); // 加载热更新程序集
  170. yield return assembliesHandle;

  171. if (assembliesHandle.Status == AsyncOperationStatus.Succeeded)
  172. {
  173. foreach (var dll in assembliesHandle.Result)
  174. {
  175. Assembly.Load(dll.bytes); // 加载程序集
  176. Debug.Log($"Loaded hot update assembly: {dll.name}");
  177. }
  178. }
  179. else
  180. {
  181. Debug.LogError("Failed to load hot update assemblies");
  182. }

  183. Addressables.Release(assembliesHandle); // 释放资源句柄
  184. }

  185. private IEnumerator LoadScene(string sceneName)
  186. {
  187. yield return new WaitForSeconds(1); // 延迟1秒加载场景
  188. var handle = Addressables.LoadSceneAsync(sceneName); // 加载场景
  189. yield return handle;

  190. if (handle.Status == AsyncOperationStatus.Succeeded)
  191. {
  192. Debug.Log($"Scene {sceneName} loaded successfully");
  193. }
  194. else
  195. {
  196. Debug.LogError($"Failed to load scene {sceneName}");
  197. }
  198. }

  199. private IEnumerator LoadPlayer()
  200. {
  201. var handle = Addressables.LoadAssetAsync<GameObject>("Player"); // 加载Player对象
  202. yield return handle;

  203. if (handle.Status == AsyncOperationStatus.Succeeded)
  204. {
  205. Instantiate(handle.Result); // 实例化Player对象
  206. Debug.Log("Player instantiated successfully");
  207. }
  208. else
  209. {
  210. Debug.LogError("Failed to load player");
  211. }

  212. Addressables.Release(handle); // 释放资源句柄
  213. }

  214. private IEnumerator LoadCube()
  215. {
  216. yield return LoadAsset<GameObject>("Cube"); // 加载并实例化Cube对象
  217. yield return new WaitForSeconds(3.0f);

  218. }

  219. private IEnumerator LoadAsset<T>(string address) where T : UnityEngine.Object
  220. {
  221. // 等待一段时间来模拟资源加载速度
  222. yield return new WaitForSeconds(3.0f);

  223. // 异步开始加载资源
  224. AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(address);

  225. // 在加载过程中更新进度条
  226. while (!handle.IsDone)
  227. {
  228. float progress = handle.PercentComplete;
  229. UpdateProgressBar(progress); // 更新进度条
  230. yield return null;
  231. }

  232. // 检查资源加载状态
  233. if (handle.Status == AsyncOperationStatus.Succeeded)
  234. {
  235. // 访问已加载的资源
  236. T loadedAsset = handle.Result;
  237. Debug.Log($"{typeof(T).Name} loaded successfully: {loadedAsset.name}");

  238. // 如果是 GameObject 类型的资源,则实例化它
  239. if (loadedAsset is GameObject)
  240. {
  241. Instantiate(loadedAsset as GameObject);
  242. }
  243. }
  244. else
  245. {
  246. Debug.LogError($"Failed to load asset at address: {address}");
  247. }

  248. // 完成后释放资源句柄 注释掉释放资源句柄,否则会导致材质丢失
  249. //Addressables.Release(handle);
  250. }

  251. private IEnumerator LoadAssetA<T>(string address) where T : UnityEngine.Object
  252. {
  253. var handle = Addressables.LoadAssetAsync<T>(address); // 加载指定地址的资源
  254. yield return handle;

  255. if (handle.Status == AsyncOperationStatus.Succeeded)
  256. {
  257. T loadedAsset = handle.Result; // 获取加载的资源
  258. Debug.Log($"{typeof(T).Name} loaded successfully: {loadedAsset.name}");

  259. if (loadedAsset is GameObject)
  260. {
  261. Instantiate(loadedAsset as GameObject); // 实例化GameObject类型的资源
  262. }
  263. }
  264. else
  265. {
  266. Debug.LogError($"Failed to load asset at address: {address}");
  267. }

  268. Addressables.Release(handle); // 释放资源句柄
  269. }

  270. // 存储资源句柄的列表
  271. private List<AsyncOperationHandle> activeHandles = new List<AsyncOperationHandle>();

  272. // 泛型方法,用于加载指定地址的资源并显示进度条
  273. private IEnumerator LoadAssetB<T>(string address) where T : UnityEngine.Object
  274. {

  275. // 异步开始加载资源
  276. AsyncOperationHandle<T> handle = Addressables.LoadAssetAsync<T>(address);
  277. activeHandles.Add(handle); // 将句柄添加到列表中

  278. // 在加载过程中更新进度条
  279. while (!handle.IsDone)
  280. {
  281. float progress = handle.PercentComplete;
  282. UpdateProgressBar(progress); // 更新进度条
  283. yield return null;
  284. }

  285. // 检查资源加载状态
  286. if (handle.Status == AsyncOperationStatus.Succeeded)
  287. {
  288. // 访问已加载的资源
  289. T loadedAsset = handle.Result;
  290. Debug.Log($"{typeof(T).Name} loaded successfully: {loadedAsset.name}");

  291. // 如果是 GameObject 类型的资源,则实例化它
  292. if (loadedAsset is GameObject)
  293. {
  294. Instantiate(loadedAsset as GameObject);
  295. }
  296. }
  297. else
  298. {
  299. Debug.LogError($"Failed to load asset at address: {address}");
  300. }

  301. }

  302. // 调用该方法以释放所有资源句柄
  303. public void ReleaseAllHandles()
  304. {
  305. foreach (var handle in activeHandles)
  306. {
  307. Addressables.Release(handle);
  308. }
  309. activeHandles.Clear();
  310. }
  311. }
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

10

主题

10

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
10148