暂时处于初步研究状态,目前的框架使用还是尚少,本篇文章旨在同步给大家大概的使用流程和使用心得,在初步建立新项目时可以适当考虑。
与强制更新相对应,移动平台上App的可执行程序没有发生变化,仅需要更新游戏资产就可以实现新版本的分发,这种更新称之为热更新。由于不需要经过App商店审核,这种更新内容可以非常快速地分发给玩家。玩家也不需要重新下载App全量包,仅需要下载变动的资产部分即可正常进行游戏,相对而言减小了玩家流失的风险。
IOS上的反射是部分支持,支持使用反射读取源代码,但不支持使用反射动态生成可执行代码。总而言之,IOS不支持以动态方式创建新的方法和类型。资源热更新采用我们目前的assetbundle是没有任何问题的,如果我们把部分逻辑提取至一个单独的代码库工程中,打包为DLL,再用DLL打包成AB包,用户下载完这个AB包后动态加载DLL文件,按理说是可行的,但是ios不支持动态运行代码,所以是没办法的。
目前市面上主流的,我了解到的热更新方案有lua、ILRunTime、HybridCLR。
从unity到底层二进制代码的流程主要是Unity->.net字节码->il2cpp->C++静态代码->二进制代码
lua分xlua和ulua。xlua是腾讯写的lua热更新框架,目前项目在使用,主要逻辑是自己会生成一个虚拟机管理lua数据,在初始阶段用dostring或者loader的方式加载lua,中期通过挂载luabehaviour的方式和lua侧的luatable进行交流,中间有关unity的obj生成wrap文件让lua侧能get、set数据。具体参考前文Lua与C#交互初析-CSDN博客。
ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行环境,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新。由于unity可以解释执行.net字节码,所以ilruntime采取C#侧的一个虚拟环境来对C#代码作.net字节码更新,交给unity。
HybridCLR改写了il2cpp模块,给il2cpp新增了原生的interpreter模块,使其能够通过Assembly.Load的方式动态加载dll。没有数据跨域的问题。
简要概括,为了能让游戏运行过程中动态更新代码,有两种流派:lua和ILRuntime在unity内部实现一个虚拟机,用虚拟机解释执行.net代码,在这里面进行代码热更;hybridCLR改写了更底层的il2cpp模块,我们在运行时只需要确保更新到最新的.dll模块,即可完成代码更新。
在说明代码热更新之前,首先需要研究AB包的热更新流程。打包方式采用BuildPipeline.BuildAssetBundles实现,在运行过程中调用AB包资源时采用AssetBundle.LoadFromFile/AssetBundle.LoadFromMemory 和 AssetBundle.LoadAsset实现。
为了模拟热更新,客户端本地需要在网络上请求到最新的资源并且下载到本地,再用新下载的文件加载进游戏中才算真正实现。于是服务器端我临时搭建了一个python的http服务器用来处理简单的上传下载功能。客户端采用项目里的下载流程。
根据官方文档的流程做,新建一个2021稳定版本的unity工程,在内部安装HybridCLR package并初始化HybridCLR,并对playerSettings做出一些改动(比如关闭增量式GC(Use Incremental GC) 选项、Scripting Backend 切换为 il2cpp等操作)。具体流程参照官方文档。
在进程刚启动时就更新主链接库Assembly-CSharp.dll,参考以下代码,时间类似于项目组目前的startup更新,只不过这里更新的是dll文件,组内更新的是assetbundle然后解包注入lua bytes。
load完Assembly-CSharp.dll就直接开始走内部加载方式了,直接在热更新的代码片段里驱动调用更新代码把其他资源更新过来即可。
最后就可以开展其他工作了,比如在这些资源上增删改查代码。
更新前:
更新后:
如果后期要增加外部dll(大部分时候用Assembly-CSharp.dll就已经可以了),也没啥问题,直接在热更新C#代码里更新热更资源就行。比如现在我增加一个新的dll,只需要导入进来并且设置project的HybridCLR settings即可(类似assetbundle kit设置)。
导入ClassLibrary1.dll后改写上述的hotupdatemain即可。
这个时候不用换包,还是用之前的那个包,依然可以达到热更新效果。