Android平台Unity3D游戏的代码热更新

发表于2016-05-05
评论1 4.1k浏览

原文链接:http://blog.uwa4d.com/archives/HotFix.html


代码热更新在项目开发中起着举足轻重的作用:开发期间能减少版本迭代次数、提升程序调试效率、大大缩短开发周期;运营期间降低大版本更新频率、减少用户流失、提高留存付费等。因此,代码热更新已经成为项目开发中几乎必不可少的环节。

Unity引擎组件化设计及跨平台发布的特性为游戏开发带来了极大的便利,但天然不支持代码热更新是国内开发者都头痛的问题。今天为大家介绍的方法主要解决Unity游戏在Android平台的C#代码热更新问题。




跨平台和多语言支持


Unity之所以能跨平台发布,依赖于其底层运行的是Mono。Mono可以简单地理解为开源版本的.Net框架(基于C#和CLI的ECMA标准),当微软开源了.Net的核心库后,Mono也在做相应的改善和优化。Unity基于Mono支持三种脚本语言:C#、Boo和UnityScript,其中 UnityScript是JavaScript的强类型的变种,开发人员使用的最多的还是C#和UnityScript。这里的脚本并不是传统意义上的脚本即解释执行,而是将代码编译成IL后在Mono的Runtime上运行(类似于Java的运行原理),因此Unity并不支持传统意义的游戏脚本编程。而iOS 平台的IL2CPP的出现,意味着Unity中游戏编程语言的静态化的发展,所以目前我们推测Unity不会原生支持动态语言(Lua)。



为何不能代码热更新


1. 资源和脚本

对于Stealth项目中的一个Prefab文件“door_generic_slide.prefab”,其在Inspector中的详情如下:

可以看出此Prefab有6个组件,其中1个组件是脚本,且脚本中含有可序列化的变量(只要能在Inspector中显示的都是可序列化的变量)。在文件目录中找到该文件并打开,如果Unity Editor是默认设置,则我们会发现该二进制文件根本打不开。可以通过两个途径将文件转换成文本文档:

运行Unity安装目录下的binary2text.exe文件("C:Program Files (x86)UnityEditorDataToolsbinary2text.exe");
在Editor中Edit -> Project Settings -> Editor -> Asset Serialization设置Mode为Force Text。

从该文件可以清楚地记录着DoneDoorAnimation脚本和该资源的依赖关系。

脚本中的序列化值写在资源里"Required Key = 0" ,这就使得资源和脚本之间产生了强关联。要想对代码进行热更新,首先要去除这层关系。


2. 资源都可以热更新,代码为什么不行

Unity 中提供的AssetBundle可以对资源打包进行热更新,遗憾的是AssetBundle并没有自动将脚本打包进去,所以不能通过AssetBundle不能对代码进行热更新。



如何进行热更新


1. Lua热更新
目前比较流行的是基于Lua的热更新,如ULua(http://www.ulua.org/)解决了Unity游戏中代码热更新。但基于Lua的热更新或多或少会带来额外的堆内存分配和代码执行效率的损失,因此对于及时性要求很高的游戏,建议不要大面积的使用。


2. Android平台的热更新
该原理是解除资源和代码的关系,将代码编译成dll,在游戏一运行时动态加载。


(1) 分离
对于脚本我们可以简单地将脚本分为数据(变量)和逻辑(方法)两部分:例如A.cs -> Uwa4dDataA.cs和Uwa4dLogicA.cs。其中Uwa4dDataA.cs中只有成员变量而Uwa4dLogicA.cs和A.cs基本一致。分离后的问题是,依赖了A.cs的资源再也找不到A.cs。因为二者之间的依赖是通过资源文件保存的,所以只需要将资源文件的对于A.cs的依赖替换成 Uwa4dDataA.cs的依赖即可。


(2) 将Uwa4dLogicA.cs编译成Dll
首先将Uwa4dDataA.cs编译成Assembly-CSharp.dll,然后编译Uwa4dLogicA.cs(依赖Assembly-CSharp.dll),另外要注意编译时.Net的兼容版本。


(3) 加载Uwa4dLogicA.cs
加载Uwa4dLogicA.cs编译后的DLL,获得Uwa4dLogicA.cs后通过AddComponent将其挂在相应的资源上,并利用Uwa4dDataA.cs对于其数据进行初始化。



优劣对比


对于我们推荐的ULua和 Android平台的热更新这两种方法,由于iOS平台不允许动态加载库,所以后者方法是行不通的,我们建议二种更新方法结合使用。对于UI更新使用ULua;对于即时战斗逻辑、游戏结果核算等使用本文提及的Android热更新方法。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引