前言:记录了总6w字的面经知识点,文章中的知识点若想深入了解,可以点击链接学习。由于文本太多,按类型分开。这一篇是 热更新 常问问题总结,有帮助的可以收藏。
热更新本身对于资源热更新是非常容易的,Unity自带的AB包就可以轻松解决,难的是代码热更新,因为Unity中的C#是编译型语言,Unity在打包后,会将C#编译成一种中间代码,再由Mono虚拟机编译成汇编代码供各个平台执行,它打包以后就变成了二进制了,会跟着程序同时启动,就无法进行任何修改了。
LUA是解释型语言,并不需要事先编译成块,而是运行时动态解释执行的。这样LUA就和普通的游戏资源如图片,文本没有区别,因此可以在运行时直接从WEB服务器上下载到持久化目录并被其它LUA文件调用。
1.2 不用C#热更的原因
准确的说,C#在安卓上可以实现热更新,但在苹果上却不能。
那C#为什么不做成解释型语言呢?因为C#的定位是一个追求效率且功能强大的编译型语言。在安卓上可以通过C#的语言特性-反射机制实现动态代码加载从而实现热更新。
具体做法是:将需要频繁更改的逻辑部分独立出来做成DLL,在主模块调用这些DLL,主模块代码是不修改的,只有作为业务(逻辑)模块的DLL部分需要修改。游戏运行时通过反射机制加载这些DLL就实现了热更新。
但苹果对反射机制有限制,不能实现这样的热更。为什么限制反射机制?安全起见,不能给程序太强的能力,因为反射机制实在太过强大,会给系统带来安全隐患。
扩展:
原因链接:谁偷了我的热更新?Mono,JIT,iOS - 慕容小匹夫 - 博客园前言 由于匹夫本人是做游戏开发工作的,所以平时也会加一些玩家的群。而一些困扰玩家的问题,同样也困扰着我们这些手机游戏开发者。这不最近匹夫看自己加的一些群,常常会有人问为啥这个游戏一更新就要重新下载,而https://www.cnblogs.com/murongxiaopifu/p/4278947.html
ILRuntime(C#热更新:
lLRuntime项目为基于C#的平台(例如Unity)提供了一个纯以实现,快速、防便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新。
编译原理:它把代码分为两个dll文件,启动的时候只启动一个,另一个dll通过反射启动,在没启动第二个热更的dll,将第一个dll文件替换掉。来达到一个热更的效果。
1.查找指定文件夹ABResource里的资源文件
2.使用Unity自带的BuildPipeline进行构建AB包
1. 整包
将完整更新资源放在Application.StreamAssets目录下,首次进入游戏将资源释放到Application.persistentDataPath下。
优点:首次更新小。
缺点:安装包下载时间长,首次安装久。
2. 分包
少部分资源放在包里,其他资源存放在服务器上,进入游戏后将资源下载到Application.persistentDataPath目录下。
优点:安装包小,安装时间短,下载快。
缺点:首次更新下载解压包时间久。
3. 适用性
海外游戏大部分是使用分包策略,平台规定
国内游戏大部分是使用整包策略
总的来说,就是每个大版本有个母包资源,之后再有热更新都是重新打AssetBundle和母包资源做比较,差异资源即是需要的热更新资源。Lua这种特殊的大文件更新,会生成—个新的ab包,实际加载lua文件会先从新的ab包中索引一次,找不到再去原来的ab包中查找,降低热更新包的大小。
AssetBundle是将资源使用Unity提供的一种用于存储资源的压缩格式打包后的集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。
用途:
(1)创建Asset bundle,开发者在unity编辑器中通过脚本将所需要的资源打包成AssetBundle文件。
(2)上传服务器。开发者将打包好的AssetBundle文件上传至服务器中。使得游戏客户端能够获取当前的资源,进行游戏的更新。
(3)下载AssetBundle,首先将其下载到本地设备中,然后再通过AsstBundle的加载模块将资源加到游戏之中。
(4)加载,通过Unity提供的API可以加载资源里面包含的模型、纹理图、音频、动画、场景等来更新游戏客户端。
(5)卸载AssetBundle,卸载之后可以节省内存资源,并且要保证资源的正常更新。
特定平台的资产解压包,有点类似于压缩文件(资产:模型、预设体、材质球、贴图、音效等等)
1.相较于Resources下的资源,AB包更好的管理资源;减少包体大小(压缩资源和减少初始包大小)
2.进行资源的热更新(资源的热更新和脚本热更新)
3.补充点:热更新规则
1.AB包文件:资源文件
2.mainfest文件:是AB包信息文件,当加载的时候,提供了关键信息,如资源信息(ab包路径,CRC冗余码)、依赖关系、版本信息等
3.关键AB包(和目录名字一样的包) :主包 包含了依赖关系
LZMA格式
使用LZMA格式压缩的AssetBundle的包体积最小(高压缩比),但是相应的会增加解压缩时的时间。
LZ4格式
压缩后的AssetBundle包体的体积较大(该算法基于chunk)。但是使用LZ4格式的好处在于解压缩的时间相对要短。
不压缩
没有经过压缩的包体积最大,但是访问速度最快。
BuildPipline.BuildAssetBundle
1.先获取WWW对象,再通过WWW.assetBundle获取AssetBundle对象(已弃用)
2.直接获取AssetBundle (5.3之后LoadFromFile,LoadFromMemory,
LoadFromMemoryAsync并增加了LoadFromFileAsync)
1.解析版本文件列表
2.加载资源
Unity提供了三个不同的API从AssetBundles加载UnityEngine.Objects,这些API都绑定到AssetBundle对象上,并且这些API具有同步(和异步变体):
并且这些API的同步版本总是比异步版本快至少一个帧(其实是因为异步版本为了确保异步,都至少延迟了1帧),异步加载每帧会加载多个对象,直到它们的时间切片切出。
AssetBundle.Unload(false):内存中的AssetBundle对象包含的资源会被销毁。
AssetBundle.Unload(true):不仅仅内存中的AssetBundle对象包含的资源会被销毁。根据这些资源实例化而来的游戏内的对象也会销毁。
Reources.UnloadAsset(Object):显式的卸载已加载的Asset对象,只能卸载磁盘文件加载的Asset对象Resources。
UnloadUnusedAssets:用于释放所有没有引用的Asset对象
Destroy:主要用于销毁克隆对象,也可以用于场景内的静态物体,不会自动释放该对象的所有引用。虽然也可以用于Asset,但是概念不一样要小心,如果用于销毁从文件加载的Asset对象会销毁相应的资源文件!但是如果销毁的Asset是Copy的或者用脚本动态生成的,只会销毁内存对象。
1,手游是快节奏的应用,功能和资源更新频繁,特别是重度手游安装包常常接近1个G,如果不热更新,哪怕改动一行代码也要重新打个包上传到网上让玩家下载。
2,对于IOS版本的手游包IPA,要上传到苹果商店进行审核,周期漫长,这对于BUG修复类操作是个灾难。
基于以上两点,热更新就很重要了,快速,小巧,绕过苹果审核。
客户端版本号我们是 4 位来标识,假设是 X.Y.Z.W,下面是 XYZW值对应的意义:
版本号 |
对应介绍 |
X:【巨大版本号】 |
这一位其实就是 1,没事一般不会动它,除非有太巨大的变化,目前反正还是 1; |
Y:【整包更新版本号】 |
我们游戏一般一个月会有一个比较大的版本迭代,这种版本会走商店,每次提交Y值+1; |
Z:【服务器协议版本号】 |
一个月度版本周期内,万一 SDK 有问题或者 C#层有发现 bug,需要更新商店,这一位会+1,这里单独留一个 Z 处理这种商店版本号,是因为不想影响 Y 值,而商店提交新包要求版本号必须有增加,buildNum 也是商店要求必须要升的; |
W:【编译版本号\热更版本号】 |
每次热更都+1 |
【第 2 位加 1 之后,3、4 位全部清 0】
比如目前商店版本号是 1.1.0.0,这个版本我们热更了 3 次后,版本 号就变成 1.1.0.3,这时候发现好像 C#层有一点 bug 必须要修复,那打 一个 1.1.1.3 提交商店,1.1.1.3 包里的资源和 1.1.0.3 的资源是一模一样的,这之后如果有第 4 次热更,那热更包的版本号就是 1.1.1.4。
加入设计了很多很多的界面,可以在游戏加载开始之前先将界面打成ab包,然后在游戏中设计逻辑,用到哪个界面根据索引加载显示即可。