Unity游戏开发客户端面经——热更新(初级)

前言:记录了总6w字的面经知识点,文章中的知识点若想深入了解,可以点击链接学习。由于文本太多,按类型分开。这一篇是 热更新 常问问题总结,有帮助的可以收藏。


1. 热更新的原理

1.1 为什么使用Lua作为热更新语言,不用C#

        热更新本身对于资源热更新是非常容易的,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文件替换掉。来达到一个热更的效果。

2. 热更新的流程

2.1 导出热更流程

  1. 打包热更资源的对应的md5信息(涉及到增量打包)
  2. 上传热更 ab 到热更服务器 
  3. 上传版本信息到版本服务器 

2.2 游戏热更流程

  1. 启动游戏。
  2. 根据当前版本号,和平台号去版本服务器上检查是否有热更。
  3. 从热更服务器上下载 MD5 文件,比对需要热更的具体文件列表。
  4. 从热更服务器上下载需要热更的资源,解压到热更资源目录。
  5. 游戏运行加载资源,优先到热更目录中加载,再到母包资源目录加载。

3. 资源打包/依赖列表生成

1.查找指定文件夹ABResource里的资源文件

  1. Directory.GetFile(资源路径)
  2. 新建AssetBundleBuild对象
  3. 获取资源名称,并赋值对应AB名称
  4. 获取各个资源的依赖项:通过UnityEditor.AssetDataBase类获取各个资源的依赖项

2.使用Unity自带的BuildPipeline进行构建AB包

  1. BuildPipeLine.BuildAssetBundles(输出AB包路径)。
  2. File.WriteAllLines(将依赖项写入文件里) 

4. 打包方案

1. 整包

        将完整更新资源放在Application.StreamAssets目录下,首次进入游戏将资源释放到Application.persistentDataPath下。

        优点:首次更新小。

        缺点:安装包下载时间长,首次安装久。

2. 分包

        少部分资源放在包里,其他资源存放在服务器上,进入游戏后将资源下载到Application.persistentDataPath目录下。

        优点:安装包小,安装时间短,下载快。

        缺点:首次更新下载解压包时间久。

3. 适用性

        海外游戏大部分是使用分包策略,平台规定

        国内游戏大部分是使用整包策略 

5. 打包逻辑

        总的来说,就是每个大版本有个母包资源,之后再有热更新都是重新打AssetBundle和母包资源做比较,差异资源即是需要的热更新资源。Lua这种特殊的大文件更新,会生成—个新的ab包,实际加载lua文件会先从新的ab包中索引一次,找不到再去原来的ab包中查找,降低热更新包的大小。

6. AssetBundle介绍

        AssetBundle是将资源使用Unity提供的一种用于存储资源的压缩格式打包后的集合,它可以存储任何一种Unity可以识别的资源,如模型,纹理图,音频,场景等资源。也可以加载开发者自定义的二进制文件。

用途:

  1. 制作DLC (动态的可下载内容)
  2. 减少初始包大小
  3. 加载为用户平台优化的资源
  4. 减少运行时的内存压力

7. AssetBundle的具体开发流程

(1)创建Asset bundle,开发者在unity编辑器中通过脚本将所需要的资源打包成AssetBundle文件。

(2)上传服务器。开发者将打包好的AssetBundle文件上传至服务器中。使得游戏客户端能够获取当前的资源,进行游戏的更新。

(3)下载AssetBundle,首先将其下载到本地设备中,然后再通过AsstBundle的加载模块将资源加到游戏之中。

(4)加载,通过Unity提供的API可以加载资源里面包含的模型、纹理图、音频、动画、场景等来更新游戏客户端。

(5)卸载AssetBundle,卸载之后可以节省内存资源,并且要保证资源的正常更新。

8. AB包

8.1 AB包是什么

        特定平台的资产解压包,有点类似于压缩文件(资产:模型、预设体、材质球、贴图、音效等等)

8.2 AB包的作用

        1.相较于Resources下的资源,AB包更好的管理资源;减少包体大小(压缩资源和减少初始包大小)

        2.进行资源的热更新(资源的热更新和脚本热更新)

        3.补充点:热更新规则

8.3 AB包生成的资源文件

        1.AB包文件:资源文件

        2.mainfest文件:是AB包信息文件,当加载的时候,提供了关键信息,如资源信息(ab包路径,CRC冗余码)、依赖关系、版本信息等

        3.关键AB包(和目录名字一样的包) :主包 包含了依赖关系

9. AssetBundle的压缩格式

        LZMA格式

        使用LZMA格式压缩的AssetBundle的包体积最小(高压缩比),但是相应的会增加解压缩时的时间

        LZ4格式

        压缩后的AssetBundle包体的体积较大(该算法基于chunk)。但是使用LZ4格式的好处在于解压缩的时间相对要短

        不压缩

        没有经过压缩的包体积最大,但是访问速度最快

10. AssetBundle的接口

        BuildPipline.BuildAssetBundle

11. AssetBundle对象的获取方式

        1.先获取WWW对象,再通过WWW.assetBundle获取AssetBundle对象(已弃用)

        2.直接获取AssetBundle (5.3之后LoadFromFile,LoadFromMemory,

        LoadFromMemoryAsync并增加了LoadFromFileAsync)

12. 解析文件/加载AB包资源

1.解析版本文件列表

  1. File.ReadAllLines(读取文件列表资源路径URL)
  2. 获取资源名称,获取AB包名称,获取依赖项,字典容器存储。
  3. 获取Lua文件

2.加载资源

  1. 异步加载资源AB包,AssetBundleRequest请求,AssetBundle.LoadFromFileAsync。
  2. 先检查依赖项,再异步加载AB包依赖项
  3. 加载成功后都有对应的回调方法,将资源作为参数传入

13. AssetBundle对象的加载方式

        Unity提供了三个不同的API从AssetBundles加载UnityEngine.Objects,这些API都绑定到AssetBundle对象上,并且这些API具有同步(和异步变体):

  1. LoadAsset(LoadAssetAsync):从资源包中加载指定的资源
  2. LoadAllAssets (LoadAllAssetsAsync):加载当前资源包中所有的资源
  3. LoadAssetWithSubAssets (LoadAssetWithSubAssetsAsync)

        并且这些API的同步版本总是比异步版本快至少一个帧(其实是因为异步版本为了确保异步,都至少延迟了1帧),异步加载每帧会加载多个对象,直到它们的时间切片切出。

14. 资源卸载

        AssetBundle.Unload(false):内存中的AssetBundle对象包含的资源会被销毁。

        AssetBundle.Unload(true):不仅仅内存中的AssetBundle对象包含的资源会被销毁。根据这些资源实例化而来的游戏内的对象也会销毁。

        Reources.UnloadAsset(Object):显式的卸载已加载的Asset对象,只能卸载磁盘文件加载的Asset对象Resources。

        UnloadUnusedAssets:用于释放所有没有引用的Asset对象

        Destroy:主要用于销毁克隆对象,也可以用于场景内的静态物体,不会自动释放该对象的所有引用。虽然也可以用于Asset,但是概念不一样要小心,如果用于销毁从文件加载的Asset对象会销毁相应的资源文件!但是如果销毁的Asset是Copy的或者用脚本动态生成的,只会销毁内存对象。

15. 手游为什么需要热更新

        1,手游是快节奏的应用,功能和资源更新频繁,特别是重度手游安装包常常接近1个G,如果不热更新,哪怕改动一行代码也要重新打个包上传到网上让玩家下载。

        2,对于IOS版本的手游包IPA,要上传到苹果商店进行审核,周期漫长,这对于BUG修复类操作是个灾难。

        基于以上两点,热更新就很重要了,快速,小巧,绕过苹果审核。

16. 版本号的管理

        客户端版本号我们是 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。

17. Ab包的应用

        加入设计了很多很多的界面,可以在游戏加载开始之前先将界面打成ab包,然后在游戏中设计逻辑,用到哪个界面根据索引加载显示即可。

你可能感兴趣的:(Unity客户端开发面经,热更新,unity)