AB包

发布时间 2023-12-27 20:09:33作者: mc宇少

1.AB包内部结构

AB包简单说就是一串二进制数据,这串数据存的是资源(预制、材质、贴图、模型等)。这串数据分为:

1.头文件

AssetbundleFileHeader

记录这个AB的信息:版本号、压缩格式等信息。

AssetFileHeader

记录AB的文件信息(一个文件列表、记录了每个资源的name、offset、length等信息,后面读取具体资源的使用用)

2.具体的资源

头文件后面跟着的是n段数据,一段数据就是一个资源(也有一些信息、其中比较重要的就是资源类型id(0:Object、1:GameObject等)、对外部资源的引用情况)。

2..mainfest文件

。。。

3.AB变体

简单说就是一个资源的分类标签,如同一个图片的高清和低清资源。

4.压缩格式

LZMA和LZ4

LZMA需要完整解压之后才能加载包内资源(但是压缩出来的包体小),LZ4不需要完整解压就可以加载包内资源(数据被分为大小相同的块,并被分别压缩,加载时只加载Header,用哪块的数据就解压对应的块就可以,内存占用小)。

默认使用LZMA压缩方式,因为第一次解压之后,该包又会使用LZ4再次压缩。(这就是为什么第一次加载时间长,后面就没这么长了)

5.获取AB包

AssetBundle.LoadFromFile(string path)
AssetBundle.LoadFromFileAsync(string path)

最常用的方法,从文件中加载AB包,它从一个给定的路径来加载AB包。如果AB包是LZ4加载方式,它只会加载AB包的Header,之后需要什么资源再加载那部分的AB包chunk。极大的减少了内存占用。

AssetBundle.LoadFromStream(Stream stream)
AssetBundle.LoadFromStreamAsync(Stream stream)

不咋用,从流中加载AB包,它从一个Stream中加载AB包。跟LoadFromFile一样,如果AB包是LZ4加载方式,它也是只会加载AB包的Header。(LoadFromStreamAsync是它的异步版本)

AssetBundle.LoadFromMemory(byte[] binary)
AssetBundle.LoadFromMemoryAsync(byte[] binary)

一般是网络接口用,LoadFromMemory是从内存中加载AB包,它从内存中的byte[]中加载AB包。它会完整的把AB包加载出来,适用于ab包已经被加载进内存的情况(参数是byte数组)。

6.内存占用

 现在的主流做法是在进游戏之前下载热更的包并存在本地,所以不会有WebStream这部分,当然,如果是运行时下载资源就还是要用到这个。

Assetbundle的内存占用:压缩的AB包 + 解压缩的AB包(也就是具体的资源,LZ4的不会全解,只解要用的部分)+ 实例化的资源。

三个ID

一份Asset对应一个meta文件,meta的id是GUID。

而localID是Asset内部的id,比如一个预制体有两个子节点,localID用来标记这两个子节点,Unity用它来记录资源内部的具体引用/层级关系

InstanceID是由这两个ID计算得到的全局唯一的

7.资源卸载

1.AssetBundle.Unload(false) : 卸载掉紫色部分(压缩的AB包)。会解除紫色部分和绿色部分的链接关系(Instance ID的GUID和Local ID引用变无效),再次加载一个紫色部分,原来的绿色部分不会受新加载的紫色部分的管理。因此如果不注意的话可能会导致一些不可控的问题,Unity中有Resources.UnloadUnusedAssets()方法可以很好地解决这个问题。

2.AssetBundle.Unload(true) : 卸载掉紫色部分(压缩的AB包)以及绿色部分(具体的Asset),注意这个举动可能会引发最上面的区域(实例化区)引用的资源丢失

3.Resources.UnloadUnusedAssets : 卸载掉没有引用的绿色部分(压缩的AB包)。

8.依赖问题

依赖问题,通俗的话来说就是A包中某资源用了B包中的某资源。然而如果A包加载了,B包没有加载,这就会导致A包中的资源出现丢资源的现象。

所以要尽量避免加载A包之前还要加载B包的问题。

在Unity5.0后,BuildAssetBundleOptions.CollectDependencies永久开启,即Unity会自动检测物体引用的资源并且一并打包,防止资源丢失遗漏的问题出现,但会导致资源冗余(同一份资源被打进不同的包中)。

最好就是将一些公共资源单独打到一个ab包中,并提前加载完成,尽可能减少AB包的依赖。

todo Tools.AnarysicsResources

9.细粒度问题

细粒度问题即每个AB包分别放入多少资源的问题,一个好的策略至关重要。
加载资源时,先要加载AB包,再加载资源。如果AB包使用了LZMA或LZ4压缩算法,还需要先给AB包解压。
AB包数量较多,包内资源较少
AB包数量较少,包内资源较多
加载一个AB包到内存的时间短,玩家不会有卡顿感,但每个资源实际上加载时间变长。
加载一个AB包到内存的时间较长,玩家会有卡顿感,但之后包内的每个资源加载很快。
热更新灵活,要更新下载的包体较小。
热更新不灵活,要更新下载的包体较大。
IO次数过多,增大了硬件设备耗能和发热压力。
IO次数不多,硬件压力小。

策略:

1.建一个DaBao文件夹(里面存的是需要打热更包的文件),以文件夹为单位去打包(如果有的包体过大(超过100m),可以分开打)。

2.经常更新和不经常更新的对象拆分到不同的AB包中。

3.同时加载的对象放在一个AB包中。

4.不可能同时加载的对象拆分到不同的AB包中。

5.公共资源和非公共资源拆分到不同的AB包中。

6.根据项目逻辑功能来分组打AB包。

7.公共资源和非公共资源拆分到不同的AB包中。

8.依赖文件根据上面说的就行

10.资源校验

验证本地文件的hashcodeunity的hashcode(这两个code是固定的,不会随着设备的不同而变化)

散列算法就是一种以较短的信息来保证文件唯一性的标志,这种标志与文件的每一个字节都相关,而且难以找到逆向规律。

也就是说这两个code变了,这个文件也就变了(损坏了)。