前言
热更新的内容可以是美术资源,可以是代码,但相对来说,美术资源的更新不会受到约束,代码实际上是重灾区。本文介绍的主要是客户端代码热更新。
热更新对于开发者来说是一件麻烦事,特别对于看重效率、便捷性和结构的程序员来说,热更新就是运营人员的不懂技术的表现。然而,对于上线才是刚刚开始的网络游戏,特别是手游来说。热更新是极为重要的基础功能。
客户端为什么要热更新?
适应上线需求
对于手游客户端来说,受到苹果审核的约束,一次审核提交需要10~20天不等的等待时间,而这段时间,开发进度依然会推进很多。
一旦手游上线,第一个版本在玩家疯狂行为下,出点问题是必然的,所以”上线更”就成了家常便饭. 如果你要说,必须大包,无法热更,那么10~20 多天后,游戏估计就没啥人了,更别说渠道,发行投入巨大资金进行推广之下让玩家迎来的一堆bug的版本以及所谓程序员的傲慢和清高。
热调试,热开发,热发布
除了线上问题之外,由于Unity3D为了适应64位应用需求,将C#编译出的IL代码利用il2cpp第三方库编译成为c++。效率提升了倒是好,但工程编译和发布时间变得相当感人,没个1~2个小时完全搞不定. 即便加装ssd,为了修改一个bug,也不知道要等多少根烟的时间…
只要核心功能不变化的情况下,完全可以让热更新成为开发期间的好工具,lua代码修改后,马上可以在手机上看效果,没有编译,发布的时间损耗,其实反而提升了开发效率。
对于服务器来说,常见游戏类型的玩家一般在半夜的在线人数会急速下降. 但是对于比较热门的MMO,以沟通为基础的游戏,半夜也会有很多人在线。
因此传统的停服更新对于玩家的热情秒杀很大的. 想想看,屁股先锋公测停15天各位是什么感受? 所以为了玩家体验,同时保证服务器稳定的前提下。
修复一些轻微bug,用热更新再合适不过了. 所以老服务器程序员,千万不能以服务器稳定为借口而忽略了玩家体验。
技术是用来解决问题的,不是用来装X的。
怎么热更新
以下是Unity3D的几种热更新方式:
基于C#,使用动态加载Assembly反射更新代码
这种方式在安卓上完全可行,对现有架构无需大的修改,一样使用C#和Unity3D的方式进行开发
但在iOS上受到限制,因此对于全平台首发的游戏,或者双平台都要上的游戏,已经慢慢的不使用这种方法进行热更新了
基于Lua,将Lua代码视为资源,动态加载并运行
云风团队早期研究出的UniLua是基于C#编写的Lua虚拟机来运行,而且只支持字节码解释,因此无法做动态功能,效率奇低
后期,ulua的出现,彻底将Lua作为比较正统的更新方式存在. ulua基于Tolua库进行封装,添加了一些便捷封装,代码打包和基本的框架
ToLua本身是一个基于C版Lua上扩充的库,以静态链接库方式与Unity3D代码链接. 因此,可以说ToLua是跑在C层上,速度不亚于C++和Lua的组合
基于Lua的代码更新方式,无论跨任何平台都可以以同一套代码和工作流进行,因此避免很多麻烦,成为现在主流的开发方式
游戏逻辑全都用Lua写么?
做过网页和手机App的童鞋都发现,js,一个bug超多,设计奇怪的语言居然成为主流界面开发语言,为啥?
动态特性适合制作ui
另外一个反例就是: 使用C++开发界面,例如Qt,MFC之类,虽然设计严谨,但是最终挡不住各种奇葩的修改需求。
因此,界面非常推荐使用动态语言来开发,游戏界就是用Lua。
而游戏核心,根据各自游戏类型来定,总的一点,效率瓶颈点,Update之类的,尽量使用C#或者C++来实现。
写在最后
当前中国大环境下的玩家和各种氪金理由与纯的不能再纯的游戏人的基本愿望是冲突的。
然而国外游戏的各种设计和机制,暴雪战网更新不及时,版本不对没提示,这些基本错误在中国的网游都不会出现的。
技术上无法赶英超美的我们,在体验上已经输出了我们的价值观,老外们都在学。对于程序员来说,只是多贴近玩家,多了解外面的世界而已。
热更新,通俗点说就是补丁,玩家那边知道重启客户端就可以更新到了的,不用卸载重新安装app,相对于单机游戏,这也是网络游戏用得比较多的一个东西吧。
首先,大概流程如下:
编辑
luaFileList.json文件内容一般是lua文件的键值对,key为lua文件路径+文件名,value为MD5值:
游戏上线后,遇见bug或者需要更新内容(包括资源,玩法,数值调整,游戏脚本等)的时候,一般有2种做法。第一种,发个新包,然后让玩家下载新的版本;第二种,在游戏内更新,游戏启动时去下载需要更新的资源。第一种我们一般称为游戏大版本更新,第二种称为热更新。
一般来说,不能通过小版本更新解决的问题,才会用到大版本更新,例如,不可热更的游戏代码。而热更新是建立在每次大版本更新的基础上进行的更新,因此,这2种更新方式应该互相承接。
我们可以通过MD5文件和更新文件以及版本号来实现大版本更新和热更新。游戏的版本号由大版本号+资源版本号构成,每一次大版本更新,大版本号增加,资源版本号重置。每一次热更新,大版本号不变,资源版本号增加。
MD5文件(MD5File)记录每一个资源的MD5值。例如:
a.txt,1630d23f45464df6071a9948dd1592bf b.texture,f9c985a8f2a86292a024c4ed21ed33fb
版本文件(VersionFile)记录每一个更新文件的资源版本号及新的MD5值,资源版本号对应玩家去服务器上哪个版本库(路径)里下载资源,MD5值用于服务器的资源和本地资源是否一样,避免重复下载。例如:
a.txt,1630d23f45464df6071a9948dd1592bf,0.1 c.txt,2312xd23f45464df6071a9948dd1592b,0.2
热更流程
大版本更新流程
- 清除之前的Md5文件和版本文件
- 打包所有的资源
- 计算每个资源的MD5值,创建新的Md5文件,将所有资源的资源名称和对应的MD5值保存在MD5文件中。
热更新流程
- 打包所有资源
- 计算每个资源的MD5值,并和MD5文件中记录的MD5值做比较,将MD5值发生变化的资源和被删除的资源记录下来.
- 判断是否存在版本文件: 如果不存在版本文件,即现在是在大版本后的第一次热更新,创建一个版本文件,将所有MD5值发生变化的资源的名称和其版本号记录在版本文件中,格式为为【文件名,新的MD5值,当前资源版本号】。如果已经存在版本文件,即现在是在上一次热更新之后继续热更新。首先读取上一次的版本文件,然后遍历本次MD5值发生变化的资源,如果资源名称在版本文件中存在,则将版本文件中该资源版本号置为当前版本;如果资源名称在版本文件中不存在,则在版本文件中添加新的记录,存放其资源名称和当前资源版本号;如果版本文件中存在被删除的资源名称,将该资源的记录从版本文件中移除。
- 上传需要更新的资源和版本文件(VersionFile)到服务器
- 删除原来的MD5文件,将所有资源的MD5值保存在新的MD5文件中。
玩家进入游戏后,首先判断是否需要更新版本,如果是大版本更新,则提示需要重新下载安装包;如果是小版本更新,则先下载版本文件(VersionFile),然后根据版本文件中的资源名和版本号去对应的地址下载资源。
服务器下载目录结构
0.1(资源版本号文件夹) -------a.txt -------VersionFile.txt 0.2(资源版本号文件夹) -------a.txt -------c.txt -------VersionFile.txt 0.3(资源版本号文件夹) -------a.txt -------d.txt -------VersionFile.txt
假设VersionFile.txt内容为:
a.txt,1630d23f45464df6071a9948dd1592bf,0.3 c.txt,d23f45464df6071a9948dd1592bfw2sb,0.2 d.txt,30d23f45464df6071a9948dd1592bfx2,0.3
如果玩家的版本是0.1,则玩家需要去0.3的目录下下载a.txt和d.txt,去0.2的目录下下载c.txt.。
如果玩家的版本是0.2,则玩家只需要去0.3的目录下下载a.txt和d.txt。
通常通过上次下载的版本文件与这次版本文件做对比,生成需要下载的文件列表,然后去服务器下载即可。