unity热更新AB包
前言
热更新是手游平台对于游戏的一种特殊更新技术,本篇将讲解AB包的资源热更和Lua的代码热更
热更新
AssetBundle详解 - 知乎 (zhihu.com)
[Unity AssetBundle的打包 发布 下载与加载_assetbundle下载-CSDN博客](https://blog.csdn.net/newchenxf/article/details/124738469#:~:text=2.1 AB包的文件结构 1 将想要动态加载的资源添加到AssetBundle (可以有多个bundle) 2 要发布时,做打包工作 3,自己把bundle上传到服务器,服务器管理版本 4 客户端下载bundle,版本不匹配(md5校验啥的),则下载新bundle 5 加载指定路径的bundle,提取文件 6 卸载bundle, 节约内存)
AssetBundle 基础
AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包。
Unity中的AssetBundle系统是对资源管理的一种扩展,通过将资源分布在不同的AB包中可以最大程度地减少运行时的内存压力,可以动态地加载和卸载AB包,继而有选择地加载内容。
什么是AB包?
AssetBundle其实就是Unity的一种Zip文件,分为LZMA和LZ4压缩,LZMA是整体压缩,LZ4是小包压缩。通过打AB包,下载,加载,卸载等实现游戏的资源更新和管理。
LoadFromFile从本地加载到AssetBundle的内存镜像区, Asset.UpLoad(false)和(true)都是释放这里的空间,区别在于true把创建出来的也清理了。Load接口会从镜像加载出Asset
Resources.UnloadUnusedAssets()释放的就是这部分没有引用的Asset,而这部分Asset可以被上层Clone加引用的方式Instance出来, GameObject全是复制,而上面的材质,网格等都是纯引用, Destroy释放的就是GameObject本身。
说到LZMA,压缩比是最高的,但是整体来讲单次加载速度慢一些,解压后里面还是LZ4压缩的,解压后会放在内存里,占用内存。
LZ4压缩会相对的零散一些,使用的时候不解压,能直接用。
加载的时候需要把需要的资源先加载,unity不会处理依赖, AssetBundleManifest记录了所有的依赖关系,读取这个文件进行加载。
假如一个资源没有进任何的AB包,有多个bundle使用了该资源,就会复制多了造成冗余。解决就是不要有资源不在任何AB包里,确保都打进了包。
AB和Resources的比较
AssetBundle | Resources |
---|---|
资源可分布在多个包中 | 所有资源打包成一个大包 |
存储位置自定义灵活 | 必须存放在Resources目录下 |
压缩方式灵活(LZMA,LZ4) | 资源全部会压缩成二进制 |
支持后期进行动态更新 | 打包后资源只读无法动态更改 |
AB的特性
AB包可以存储绝大部分Unity资源但无法直接存储C#脚本,所以代码的热更新需要使用Lua或者存储编译后的DLL文件。
AB包不能重复进行加载,当AB包已经加载进内存后必须卸载后才能重新加载。
多个资源分布在不同的AB包可能会出现一个预制体的贴图等部分资源不在同一个包下,直接加载会出现部分资源丢失的情况,即AB包之间是存在依赖关系的,在加载当前AB包时需要一并加载其所依赖的包。
打包完成后,会自动生成一个主包(主包名称随平台不同而不同),主包的manifest下会存储有版本号、校验码(CRC)、所有其它包的相关信息(名称、依赖关系)···
1 | ManifestFileVersion: 0 //版本号 |
AB的压缩格式
格式 | 包体体积 | 速度 |
---|---|---|
不压缩 | 没有经过压缩的包体积最大 | 访问速度最快。 |
LZ4格式 | 压缩后的AssetBundle包体的体积较大(该算法基于chunk)。 | 使用LZ4格式的好处在于解压缩的时间相对要短。 |
LZMA格式 | 使用LZMA格式压缩的AssetBundle的包体积最小(高压缩比) | 相应的会增加解压缩时的时间。 |
热更新步骤
热更的基本流程可以分成2部分:
第一步:导出热更新所需资源
第二步:游戏运行后的热更新流程
第一步、导出热更新所需资源
- 打包热更资源的对应的md5信息(涉及到增量打包)
- 上传热更对应的ab包到热更服务器
- 上传版本信息到版本服务器
第二步、游戏运行后的热更新流程
启动游戏
- 根据当前版本号,和平台号去版本服务器上检查是否有热更
- 从热更服务器上下载md5文件,比对需要热更的具体文件列表
- 从热更服务器上下载需要热更的资源,解压到热更资源目录
- 游戏运行加载资源,优先到热更目录中加载,再到母包资源目录加载
更新注意:
要有下载失败重试几次机制;
要进行超时检测;
要记录更新日志,例如哪几个资源时整个更新流程失败。
md5算法工作原理
MD5算法的核心思想是将任意长度的输入数据通过一系列复杂的变换,最终生成一个128位的哈希值。这个过程可以分为以下四个主要步骤:
- 填充:MD5算法首先对输入数据进行填充,使其长度达到一个特定的长度,这是为了使原始数据的长度可以被512整除。填充的方法是在原始数据后面添加一个“1”,然后添加足够数量的“0”,最后添加一个64位的整数表示原始数据的长度。
- 初始化缓冲区:MD5算法使用了一个64位的缓冲区,分为四个16位部分,用来存储中间结果和最终结果。这四个部分被初始化为特定的常数。
- 处理分组:填充后的数据被划分为长度为512位的分组,每个分组又划分为16个32位的子分组。然后,通过一系列的位操作和模加运算,每个分组都被处理并更新缓冲区的内容。这个过程涉及四个主要的轮函数和一系列的非线性函数。
- 输出:处理完所有分组后,缓冲区中的内容就是最终的哈希值。这个哈希值是一个128位的数,通常表示为32个十六进制数。
深入解析MD5哈希算法:原理、应用与安全性-腾讯云开发者社区-腾讯云 (tencent.com)
打包AB包
打包方案
名称 | 详细 | 优点 | 缺点 | 适用性 |
---|---|---|---|---|
整包 | 将完整更新资源放在Application.StreamAssets目录下,首次进入游戏将资源释放到Application.persistentDataPath下。 | 首次更新小。 | 安装包下载时间长,首次安装久。 | 国内游戏大部分是使用整包策略 |
分包 | 少部分资源放在包里,其他资源存放在服务器上,进入游戏后将资源下载到Application.persistentDataPath目录下。 | 安装包小,安装时间短,下载快。 | 首次更新下载解压包时间久。 | 海外游戏大部分是使用分包策略 |
打包逻辑
总的来说,就是每个大版本有个母包资源,之后再有热更新都是重新打AssetBundle和母包资源做比较,差异资源即是需要的热更新资源。Lua这种特殊的大文件更新,会生成—个新的ab包,实际加载lua文件会先从新的ab包中索引一次,找不到再去原来的ab包中查找,降低热更新包的大小。
分包策略
逻辑实体分包
依据资源在项目功能块的使用位置,如UI、角色、环境和其他在生命周期中常出现的内容等分包
- 将所有 UI的纹理和布局数据分包
- 将角色的模型和动画数据分包
- 将多场景共用的纹理和模型分包
该分包方式适用于制作 DLC,可以只下载单个实体而无需下载无变化的资源,其关键点在于需要开发者清楚了解每个打包的资源所要用到的时机和位置
对象类型分包
该方式适用于针对多平台分包,例如音频文件的压缩设置在Windows和Mac OS平台一样,另外由于纹理压缩格式和设置等改变频率远低于脚本和预设体,使用该分配方式可以使AssetBundle兼容更多的Unity版本
并发内容分包
并发内容分包可理解为以关卡为分组依据,将一个关卡内独有的角色、纹理、音乐等需要在同一时机加载的内容分为一包
AB依赖加载
如果一个或者多个 UnityEngine.Objects 引用了其他 AssetBundle 中的 UnityEngine.Object,那么 AssetBundle 之间就产生的依赖关系。相反,如果 UnityEngine.ObjectA 所引用的UnityEngine.ObjectB 不是其他 AssetBundle 中的,那么依赖就不会产生。
假若这样(指的是前面两个例子的后者,既不产生依赖的情况),被依赖对象(UnityEngine.ObjectB)将被拷贝进你创建的 AssetBundle(指包含 UnityEngine.ObjectA 的 AssetBundle)。
更进一步,如果有多个对象(UnityEngine.ObjectA1、UnityEngine.ObjectA2、UnityEngine.ObjectA3……)引用了同一个被依赖对象(UnityEngine.ObjectB),那么被依赖对象将被拷贝多份,打包进各个对象各自的 AssetBundle。
如果一个 AssetBundle 存在依赖性,那么要注意的是,那些包含了被依赖对象的 AssetBundles,需要在你想要实例化的对象的加载之前加载。Unity 不会自动帮你加载这些依赖。
想想看下面的例子, Bundle1 中的一个材质(Material)引用了 Bundle2 中的一个纹理(Texture):
在这个例子中,在从 Bundle1 中加载材质前,你需要先将 Bundle2 加载到内存中。你按照什么顺序加载 Bundle1 和 Bundle2 并不重要,重要的是,想从 Bundle1 中加载材质前,你需要先加载 Bundle2。
ABManager
利用AssetBundleBrowser可以轻松实现AB包的打包工作,更重要的是如何将AB包中的资源加载出来并使用它们,Unity已经提供了一组API能够实现AB包的加载和资源的加载。
主要API
1 | //AB包加载所需相关API |
ABManager.cs
1 | using System; |
其他
手游为什么需要热更新
手游是快节奏的应用,功能和资源更新频繁,特别是重度手游安装包常常接近1个G,如果不热更新,哪怕改动一行代码也要重新打个包上传到网上让玩家下载。
对于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。
链接
【Unity面试篇】Unity 面试题总结甄选 |热更新与Lua语言 | ❤️持续更新❤️_unity lua面试-CSDN博客
Unity游戏开发客户端面经——lua(初级)_unity lua-CSDN博客