Unity3D热更设计:一款基于 HybridCLR的C#热更方案

  在这篇文章之前,可以转到我的这两篇博客:C#热更方案 HybridCLR尝鲜:Windows及Android打包、超详细的Unity3D热更新框架,附示例链接,小白也能看的懂_鹿野素材屋的博客-CSDN博客_热更新框架

  这两篇博客看完后,应该就会对热更有个大致的印象了,接下来我们要做的就是将两者合并起来,实现真正的热更。

  首先我们要在脚本加载之前加载出所有的脚本文件,MD5效验部分就不再赘叙,具体代码如下:

using HybridCLR;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;
using UnityEngine.Networking;
using System.Text;
using System.Security.Cryptography;//包含MD5库//MD5信息
[Serializable]
public class MD5Message
{public string file;//文件位置及名字public string md5;//MD5效验结果public string fileLength;//文件长度
}//MD5全部信息
[Serializable]
public class FileMD5
{public string length;//总长度public MD5Message[] files;
}public static class UrlConfig
{public static string urlPre = "http://192.168.6.123/Chief/MD5";//一些基本的网络配置}/// <summary>
/// 初始化加载dll脚本:这里如果需要的话,最好是把表格数据加载也放到最开始
/// </summary>
public class DllLogin : MonoBehaviour
{//所有引用public static List<string> AOTMetaAssemblyNames { get; } = new List<string>(){     "mscorlib.dll","System.dll","System.Core.dll",};void Start(){//先进行热更Debug.Log("热更");StartCoroutine(VersionUpdate());}#region 热更部分/// <summary>/// 进度/// </summary>public float progress;/// <summary>/// 版本更新       /// </summary>IEnumerator VersionUpdate(){Debug.Log(GetTerracePath());//这里做一些本地化处理
#if UNITY_EDITOR_WINpathName = "Android";
#elif UNITY_EDITOR_OSXpathName = "IOS";
#elif UNITY_ANDROIDpathName = "Android";
#elif UNITY_IOSpathName = "IOS";
#endifDebug.Log(UrlConfig.urlPre);var _isGet = true;var uri = HttpDownLoadUrl("Bundle.txt");Debug.Log(uri);var request = UnityWebRequest.Get(uri);yield return request.SendWebRequest();_isGet = !(request.result == UnityWebRequest.Result.ConnectionError);if (_isGet){int allfilesLength = 0;var _txt = request.downloadHandler.text;Debug.Log("拿到txt:");//这里要通过MD5效验先拿到所有的代码数据            List<BundleInfo> bims = new List<BundleInfo>();FileMD5 date = JsonUtility.FromJson<FileMD5>(_txt);var _files = date.files;//初始热更 和岛屿升级热更var _list = ReviseMD5List(_files);Debug.LogError("需要热更长度" + _list.Count);DeleteOtherBundles(_files);//删除所有不受版本控制的文件string md5, file, path;int lenth;for (int i = 0; i < _list.Count; i++){MD5Message _md5 = _list[i];//Debug.Log(_md5.file + " " + _md5.fileLength + " " + _md5.md5);file = _md5.file;path = PathUrl(file);md5 = GetMD5HashFromFile(path);//Debug.LogError(file + "  " + path + "  " + md5);if (string.IsNullOrEmpty(md5) || md5 != _md5.md5){//Debug.LogError("需要添加" + string.IsNullOrEmpty(md5) + "  " + md5 + _md5.md5 + "   " + md5 != _md5.md5);bims.Add(new BundleInfo(){Url = HttpDownLoadUrl(file),Path = path});lenth = int.Parse(_md5.fileLength);allfilesLength += lenth;}}Debug.Log(allfilesLength / (1024 * 1024));Debug.LogError("热更资源数量" + bims.Count);if (bims.Count <= 1) progress = 1;//Debug.LogError("......." + GameProp.Inst.isLandUpdate);//当有新热更资源需要更新时,自动调用岛屿升级回调??????????if (bims.Count > 0){//Debug.LogError("开始尝试更新");//if (bims.Count != 1) UIMainProp.Inst.isResUpdate = true;StartCoroutine(DownLoadBundleFiles(bims, (progress) =>{Debug.Log("自动更新中..."+progress+":"+allfilesLength);}, (isfinish) =>{if (isfinish){StartCoroutine(DownLoadAssets(this.StartGame));}elseStartCoroutine(VersionUpdate());}));}else{StartCoroutine(DownLoadAssets(this.StartGame));}}}/// <summary>/// 删除所有不受版本控制的所有文件/// </summary>void DeleteOtherBundles(/*FileMD5 _md5*/MD5Message[] _list){Debug.LogError("~~~~~~~~~~开始删除~~~~~~~");var _r = "/Android/";try{string[] bundleFiles = Directory.GetFiles(GetTerracePath() + _r, "*.*", SearchOption.AllDirectories);Debug.LogError("文件名校对 :长度" + bundleFiles.Length + "::::" + GetTerracePath());foreach (string idx in bundleFiles){try{var _s = idx.Replace(GetTerracePath() + _r, "");_s = _s.Replace(@"\", "/");//Debug.Log(idx+":"+ _s + ":" + Directory.Exists(_s));if (/*Directory.Exists(_s) &&*/ !FindNameInFileMD5(_list, _s)){Debug.LogError(idx + "即将被删除");File.Delete(idx);}}catch{Debug.Log("删除文件报错" + idx);}}}catch{Debug.Log("没有android文件夹");}//Debug.Log("~~~~~~~结束删除~~~~~~~");}static bool FindNameInFileMD5(MD5Message[] _list, string _name){foreach (var _m in _list){//Debug.LogError("文件名校对" + _m.file + ":::" + _name);if (_m.file == _name) return true;}return false;}public string GetMD5HashFromFile(string fileName){if (File.Exists(fileName)){FileStream file = new FileStream(fileName, FileMode.Open);MD5 md5 = new MD5CryptoServiceProvider();byte[] retVal = md5.ComputeHash(file);file.Close();StringBuilder sb = new StringBuilder();for (int i = 0; i < retVal.Length; i++)sb.Append(retVal[i].ToString("x2"));return sb.ToString();}return null;}/// <summary>/// 校正MD5 list/// </summary>public List<MD5Message> ReviseMD5List(MD5Message[] _list){var _checkList = new List<MD5Message>();var assets = new List<string>{"prefabs","Assembly-CSharp.dll",};foreach (var _idx in _list){        foreach (var _i in assets){var _len = _idx.file.Split('/');var _end = _len[_len.Length - 1];if (_idx.file.Contains("11")) Debug.Log(_idx.file + " ::: " + (_end.Contains(_i) + " : " + !_end.Contains("public_uitoy") + " :: " + _end != "Bundle.txt"));if (_end.Contains(_i)){_checkList.Add(_idx);break;}}}Debug.Log("检验列表" + _checkList.Count);return _checkList;}/// <summary>/// 路径比对/// </summary>  public IEnumerator DownLoadBundleFiles(List<BundleInfo> infos, Action<float> LoopCallBack = null, Action<bool> CallBack = null){//Debug.Log("开始路径对比" + infos.Count);int num = 0;string dir;for (int i = 0; i < infos.Count; i++){BundleInfo info = infos[i];Debug.LogError(info.Url + "  ::::  " + i);var uri = info.Url;var request = UnityWebRequest.Get(uri);yield return request.SendWebRequest();var _isGet = !(request.result == UnityWebRequest.Result.ConnectionError);if (_isGet){try{string filepath = info.Path;dir = Path.GetDirectoryName(filepath);if (!Directory.Exists(dir))Directory.CreateDirectory(dir);File.WriteAllBytes(filepath, request.downloadHandler.data);num++;if (LoopCallBack != null)LoopCallBack.Invoke((float)num / infos.Count);//Debug.Log(dir + "下载完成");}catch (Exception e){Debug.Log("下载失败"+e);}}else{Debug.Log("下载失败");}}if (CallBack != null)CallBack.Invoke(num == infos.Count);}/// <summary>/// 记录信息/// </summary>public struct BundleInfo{public string Path { get; set; }public string Url { get; set; }public override bool Equals(object obj){return obj is BundleInfo && Url == ((BundleInfo)obj).Url;}public override int GetHashCode(){return Url.GetHashCode();}}/// <summary>/// 获得下载的URL/// </summary>public string HttpDownLoadUrl(string _str){return UrlConfig.urlPre + pathName + "/" + _str;}public string pathName;//根据不同路径,对下载路径进行封装string PathUrl(string _str){var _path = GetTerracePath() + "/" + pathName + "/" + _str;return _path;}//获得不同平台的路径string GetTerracePath(){string sPath = Application.persistentDataPath;return sPath;}#endregionprivate static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();public static byte[] GetAssetData(string dllName){return s_assetDatas[dllName];}private string GetWebRequestPath(string asset){var path = $"{Application.streamingAssetsPath}/{asset}";if (!path.Contains("://")){path = "file://" + path;}return path;}IEnumerator DownLoadAssets(Action onDownloadComplete){var assets = new List<string>{"prefabs","Assembly-CSharp.dll",}.Concat(AOTMetaAssemblyNames);foreach (var asset in assets){string dllPath = GetWebRequestPath(asset);//这个地方改为读本地文件夹if(asset== "prefabs" || asset== "Assembly-CSharp.dll") dllPath = Path.Combine("file://" + GetTerracePath(), pathName + "/Dll/" + asset);//var dllPath = Path.Combine("file://" + GetTerracePath(), pathName + "/Dll/" + asset);Debug.Log($"start download asset:{dllPath}");UnityWebRequest www = UnityWebRequest.Get(dllPath);yield return www.SendWebRequest();#if UNITY_2020_1_OR_NEWERif (www.result != UnityWebRequest.Result.Success){Debug.Log(www.error);}
#elseif (www.isHttpError || www.isNetworkError){Debug.Log(www.error);}
#endifelse{// Or retrieve results as binary databyte[] assetData = www.downloadHandler.data;Debug.Log($"dll:{asset}  size:{assetData.Length}");s_assetDatas[asset] = assetData;}}onDownloadComplete();}void StartGame(){LoadMetadataForAOTAssemblies();#if !UNITY_EDITORvar gameAss = System.Reflection.Assembly.Load(GetAssetData("Assembly-CSharp.dll"));
#elsevar gameAss = AppDomain.CurrentDomain.GetAssemblies().First(assembly => assembly.GetName().Name == "Assembly-CSharp");
#endif//这个地方必须要有个物体,提供给游戏作为入口AssetBundle prefabAb = AssetBundle.LoadFromMemory(GetAssetData("prefabs"));GameObject testPrefab = Instantiate(prefabAb.LoadAsset<GameObject>("HotUpdatePrefab.prefab"));}/// <summary>/// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。/// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行/// </summary>private static void LoadMetadataForAOTAssemblies(){// 可以加载任意aot assembly的对应的dll。但要求dll必须与unity build过程中生成的裁剪后的dll一致,而不能直接使用原始dll。// 我们在BuildProcessors里添加了处理代码,这些裁剪后的dll在打包时自动被复制到 {项目目录}/HybridCLRData/AssembliesPostIl2CppStrip/{Target} 目录。/// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。/// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误/// foreach (var aotDllName in AOTMetaAssemblyNames){byte[] dllBytes = GetAssetData(aotDllName);// 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes);Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}");}}
}

  通过以上脚本,配合将热更资源放到对应服务器,我们可以实现代码的热更。

  接着在对应预制体身上绑定如下脚本,从而实现找到游戏中物体并正确启动的目的。

  

using HybridCLR;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 该脚本提供游戏运行初始化操作,必走
/// </summary>
public class LoginAwake : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){Debug.Log("可以进行游戏里面的一些启动操作,从而实现加载");//这里可以进行游戏里面的一些启动操作,从而实现加载//GameObject.Find("Login").GetComponent<Login>().enabled = true;gameObject.AddComponent<CreateByCode>();Debug.Log("=======看到此条日志代表你成功运行了示例项目的热更新代码=======");gameObject.AddComponent<Login>();}// Update is called once per framevoid Update(){}
}

  其中CreateByCode是官方提供的测试脚本,Login是我们自己写的初始化脚本。这里需要注意的是,启动的脚本貌似必须得继承HybridCLR,不然可能因为解释器的原因不能正确执行(可能是因为解释器启动前,不支持两套脚本读取方式)

  接着就是我们的Login脚本,这里可以做我们自己的操作,比如说一些ab包热更之类。这里我并没有使用官方加载资源的方式,毕竟使用 HybridCLR只需要解决代码热更就好了。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class Login : MonoBehaviour
{public Text loginTxt;// Start is called before the first frame updatevoid Start(){Debug.Log("启动加载");loginTxt = GameObject.Find("Login/Text").GetComponent<Text>();loginTxt.text = "启动:Http加载";}// Update is called once per framevoid Update(){}
}

  到目前为止,运行截图如下,代表我们已经成功实现了脚本热更:

~~~~~~~~~~~~~~~~~~~~~~~~~以下内容和ab包有关~~~~~~~~~~~~~~~~~~~~~~

  另外多闲聊几句,我们的热更资源一般是在分工程里运行的,ab打包工具用的是assetbundle browser,虽然这款软件的作者已经三年没更新了。

  博客采用的脚本热更方案在开发阶段基本不用管,打包的时候重新构建下就行,其实相对还比较方案吧,目前没有用到lua热更方案的项目可以考虑采用。

  有问题可以私信留言,大家共同交流进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/68858.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VRay渲染器之家装户型渲染实战记录

Vray渲染器之家装户型渲染实战记录 简要介绍&#xff1a; 首先思路是分为客户端、服务器、中间件三大块。先从客户端传入json文件给包括mesh&#xff0c;灯光&#xff0c;模型的中间件&#xff0c;其中mesh和灯光可以用已有的API调用服务器的内容&#xff0c;然后调用模型中包…

【Unity 实用插件篇】| 可视化图表插件XCharts (折线图、柱状图、饼图等)详细教学

前言【Unity 实用插件篇】| 可视化图表插件XCharts (折线图、柱状图、饼图等)详细教学一、XCharts介绍1.1 特性1.2 相关网站链接1.3 效果展示二、XCharts导入三、XCharts快速使用3.1 添加一个简单图表3.2 添加多个Seire3.3 给图表添加其他组件3.4 添加Serie组件,如给折线图区域…

食住玩|3dmax效果图大师们怎么用CR去测试效果图的渲染参数?

【本文导航】 本文所示范的步骤&#xff0c;只有5个&#xff0c;且皆有截图明示。 【解说全引导进入视频教程】 3dmax效果图CAD施工图进阶 ———————50分钟速成效果图全流程不教学更快进入视频教程————— 在corona渲染器的设置面板中&#xff0c;应该如何设置测试…

食住玩3dmax|室内设计师如何用CORONA设置成品效果图出大图的渲染参数?

【本文导航】 1、简介CR&#xff1b;2、转换CR材质方法&#xff1b;3、大图CR参数2个步骤&#xff1b;4、往期回顾。 【解说全引导进入视频教程】 3dmax效果图CAD施工图进阶 ———————50分钟速成效果图全流程不教学更快进入视频教程————— 【简介CR】前面几节课&a…

效果图渲染的几大实用技巧

效果图渲染是建筑、室内、景观、产品设计等行业中非常重要的一环。一个高质量的效果图可以让客户更好地了解和感受设计方案&#xff0c;提高设计师的竞争力。但是渲染效果的好坏和速度都取决于设计师的技巧和工具。本文将介绍几大实用技巧&#xff0c;帮助设计师更好地进行效果…

C语言实现双色球案例

双色球 1.案例描述 双色球是中国福利彩票目前的一种玩法&#xff0c;并非赌博&#xff0c;其彩票投注区分为红色球号码区和蓝色球号码区&#xff0c;每注投注号码由6个红色球和1个蓝色球号码组成。红色球号码从1-33中选择&#xff0c;蓝色球号码从1-16中选择。每期开出的红色…

历史数据双色球小工具

Python可视化界面小工具&#xff0c;可自定义历史期数&#xff0c;历史双色球数据&#xff0c;打印结果、写入excel表格、并进行简单的结果分析&#xff1b; 1、工具效果图如下图所示&#xff1a; 2、生成Excel表格数据格式如下图所示&#xff1a; 3、完整代码如下&#xff1a…

Python采集双色球历史开奖信息,看看哪个号中奖概率更大

目录标题 前言知识点:开发环境:基本流程:代码展示尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 知识点: 爬虫基本流程 requests的使用 动态数据抓包 开发环境: 解释器: python 3.8 编辑器: pycharm 2022.3 requests >>> pip install requests 第三…

华南理工大学计算机/软件 复试 经验贴整理

文章目录 最新更新2023/02/24 &#xff1a; 我能提供&#xff1f;&#xff08;21&#xff0c;22届复试全流程&#xff0c;如何找复试资料&#xff0c;一些学习技巧&#xff0c;前人经验&#xff0c;闲聊&#xff0c;会就答...&#xff09;2020年2019年其它资料来源 最新更新202…

北京交通大学计算机考研02102、02103复试经验分享

【2023考研复试重要时间节点】 2023年3月20号左右计算机学院公布复试线与复试名单。2023年3月底参加复试。2023年4月初公布拟录取名单。 【复试解读】 【进入复试数据】 【1】新冠疫情之前&#xff08;20、21、22考研&#xff09;&#xff0c;北交计算机复试一直都是包括线下…

西北工业大学网络安全考研复试经验

初试直接看我师姐的文章吧&#xff0c;传送门&#xff1a;西北工业大学网络空间安全考研经验分享_崔啊是个幸福的人的博客-CSDN博客_西北工业大学网络安全考研报考学校&#xff1a;西北工业大学学院&#xff1a;网络空间安全专业&#xff1a;847初试分数&#xff1a;391英语&am…

研究生计算机专业知识复试面试常见问题

研究生计算机专业复试面试常见问题 操作系统1. 进程和线程区别和联系2. 常见的调度算法3. 死锁的产生和解决4. 虚拟内存&#xff0c;页面置换算法5. 磁盘调度 数据结构1. 常见的排序算法过程和时间复杂度&#xff0c;空间复杂度2. 深度搜索和广度搜索深度搜索(DFS)广度搜索&…

计算机网络考研复试速成 - 知识点精炼 - 背诵版

计算机网络复试速成 针对于计算机考研复试 - 计算机网络 &#xff0c;删除了很多初试中关于 计算、冗余 的内容&#xff0c;把复习中心放在 高频知识点 (偏向概念) &#xff0c;希望可以节约准研究生们的复习时间&#xff01;大家可以放心食用&#x1f356;&#x1f356;&#…

陕西师范大学计算机考研复试,复试干货 | 陕西师范大学考研复试经验分享贴...

本文为陕西师范大学应用心理学方向复试经验&#xff0c;其它方向复试流程与其基本一致&#xff0c;可放心参考学习~ヾ() 一、陕师大复试分数要求 每年的情况有一些不同&#xff0c;2017年以前进入复试分数线浮动在350-360分之间&#xff0c;2018年题稍难一些&#xff0c;复试分…

南师大教育技术学初试复试调剂经验分享

南师大教育技术学考研经验分享 1 概述... 1 2 初试准备经验... 2 2.1 政治部分... 2 2.2 英语部分... 2 2.3 c语言和web部分... 2 2.4 数据结构部分... 3 2.5 教学设计部分... 3 2.6 初试资料网盘分享... 3 2.7 当你不想学习时怎么办... 3 3 复试调剂经验... 3 3.1 复试和调剂资…

上海交通大学考研复试模块小结——密码学

好久没有写博客了&#xff0c;本来说的是自从成绩出来要好好准备复试&#xff0c;结果距离成绩出来一个月了&#xff0c;复试一点动静都没有&#xff0c;那我还是一边准备相关的专业课&#xff0c;一边按专业的形式&#xff0c;把相关的专业课知识再过一遍&#xff0c;哎~真是太…

河海大学软件工程学硕考研复试经验贴

一、写在前面 想必看到这篇文章的学弟学妹都已经考完初试了&#xff0c;考得如何每个人心中各有千秋。无论如何&#xff0c;坚持将考研整个过程走下来的你们就已经是最棒的了&#xff0c;现在可以好好休息一下&#xff0c;静待考研成绩的公布了。 我写下这篇文章的目的主要是…

夏令营导师推荐信怎么写(浙大CS上岸老学姐手把手教你)

Hello&#xff0c;我又来了&#xff01;前几天分享了自己的个人陈述&#xff0c;写了一些注意事项&#xff0c;也把自己之前的模板分享给了很多学弟学妹&#xff0c;在这里祝大家早日拿到心仪offer&#xff01;老规矩&#xff0c;还是先来介绍一下我自己&#xff1a; 本人就读于…

中科院一博士论文走红,看哭众多网友

本文转载自 凤凰网 “我走了很远的路&#xff0c;吃了很多的苦&#xff0c;才将这份博士学位论文送到你的面前。二十二载求学路&#xff0c;一路风雨泥泞&#xff0c;许多不容易。如梦一场&#xff0c;仿佛昨天一家人才团聚过。” 上述文字来自于一篇博士论文的《致谢》部分。…

恭喜马斯克、纳德拉当选美国工程院院士,张宏江、方岱宁入选外籍院士

2 月 9 日&#xff0c;美国国家工程院&#xff08;National Academy of Engineering&#xff0c;NAE&#xff09;宣布了 2021 年度国家工程院增补院士名单&#xff0c;此次共有 111 名院士和 22 名外籍院士入选。目前美国工程院院士总数达到 2388 名&#xff0c;国际院士人数达…