【Unity】AB包资源管理(一)

时间:2025-03-08 20:43:29

 一、AB包的基础概念

以下引用自Unity官方手册:

AssetBundle

AssetBundle 是一个存档文件,包含可在运行时由 Unity 加载的特定于平台的非代码资源(比如模型、纹理、预制件、音频剪辑甚至整个场景)。AssetBundle 可以表示彼此之间的依赖关系;例如,一个 AssetBundle 中的材质可以引用另一个 AssetBundle 中的纹理。为了提高通过网络传输的效率,可以根据用例要求(LZMA 和 LZ4)选用内置算法选择来压缩 AssetBundle。

AssetBundle 可用于可下载内容(DLC),减小初始安装大小,加载针对最终用户平台优化的资源,以及减轻运行时内存压力。

注意: AssetBundle 可以包含代码对象实例的序列化数据,例如 ScriptableObject。但是,类定义本身被编译为项目程序集之一。当您在 AssetBundle 中加载序列化对象时,Unity 会找到匹配的类定义,创建它的实例,并使用序列化值设置该实例的字段。这意味着您可以在 AssetBundle 中向游戏中引入的新项,只要这些项不需要对您的类定义进行任何更改。

AssetBundle 中有什么?

“AssetBundle”可以指两种不同但相关的东西。

首先是磁盘上的实际文件。这称为 AssetBundle 存档。AssetBundle 存档是一个容器,就像文件夹一样,可以在其中包含其他文件。这些附加的文件包含两种类型:

  • 一个序列化文件,其中包含分解为各个对象并写入此单个文件的资源。
  • 资源文件,这是为某些资源(纹理和音频)单独存储的二进制数据块,允许 Unity 高效地在另一个线程上从磁盘加载它们。

“AssetBundle”也可以指代通过代码进行交互以便从特定 AssetBundle 存档加载资源的实际 AssetBundle 对象。该对象包含您添加到此存档文件的资源的所有文件路径的映射。

Unity官方手册

二、AB包结构

AssetBundle由两部分组成:包头和数据段。

包头:包头包含有关AssetBundle的信息,例如其标识符、压缩类型和清单(manifest:manifest是一个用对象名字做key的查找表,每个条目都提供一个字节索引,指示在AssetBundle的数据段中可以找到给定对象的位置。后面再处理资源依赖关系时候会写到这部分内容)

在大多数平台上,此查找表是作为平衡搜索树实现的。具体而言,Windows和OSX衍生平台(包括iOS)采用红黑树。因此,随着AssetBundle中资产数量的增加,构建清单所需的时间将线性增加。

数据段:数据段包含通过序列化AssetBundle中的资产而生成的原始数据。

—如果将LZMA指定为压缩方案,则压缩所有序列化资产的完整字节数组。

—如果改为指定LZ4,则单独资源的字节将被单独压缩。

—如果不使用压缩,数据段将保留为原始字节流。

在Unity 5.3之前,对象不能在资产绑定中单独压缩。因此,如果Unity 5.3之前的版本被指示从压缩的AssetBundle读取一个或多个对象,Unity必须解压缩整个AssetBundle。通常,Unity缓存AssetBundle的解压缩副本,以提高同一AssetBundle上后续加载请求的加载性能

三、AB包的加载

AssetBundles可以通过四种不同的API加载。这四个API的行为因两个标准而异:

(1)资产绑定是LZMA压缩、LZ4压缩还是未压缩

(2)正在加载资产绑定的平台

这些API是:

(异步可选)

(异步可选)

UnityWebRequest的下载HandlerAssetBundle

(在Unity 5.6或更早版本上)

3.1

Unity官方给的建议是不要使用这个API。

(ASync)从托管代码字节数组(C#中的字节[])加载AssetBundle。它总是将源数据从托管代码字节数组复制到新分配的连续本机内存块中。如果AssetBundle是LZMA压缩的,它将在复制时解压缩AssetBundle。未压缩和LZ4压缩的资产绑定将被逐字复制。

此API消耗的最大内存量将至少是AssetBundle大小的两倍:一个拷贝位于API创建的本机内存中,另一个拷贝位于传递给API的托管字节数组中。因此,从通过此API创建的AssetBundle加载的资产将在内存中复制三次:一次在托管代码字节数组中,一次在AssetBundle的本机内存副本中,第三次在GPU或资产本身的系统内存中。

备注:在Unity 5.3.3之前,此API被称为****。它的功能没有改变。

3.2.

(ASync)是一种高效的API,用于从本地存储(如硬盘或SD卡)加载未压缩或LZ4压缩的AssetBundle。

在桌面独立、控制台和移动平台上,API将只加载AssetBundle的头,并将剩余数据保留在磁盘上。AssetBundle的对象将在调用加载方法(例如)或取消引用其实例ID时按需加载。在这种情况下不会消耗多余的内存。

在Unity编辑器中,API将把整个AssetBundle加载到内存中,就像从磁盘读取字节并使用一样。如果在Unity编辑器中对项目进行了分析,则此API可能会导致在AssetBundle加载期间出现内存峰值。这不应影响设备的性能,在采取补救措施之前,应在设备上重新测试这些尖峰。

注意:在Unity 5.3或更早版本的Android设备上,当尝试从流媒体资产路径加载资产绑定时,此API将失败。此问题已在Unity 5.4中解决。有关更多详细信息,请参阅AssetBundle使用模式步骤的Distribution-shipped with project部分。

在Unity 5.3之前,此API被称为****。它的功能没有改变。

3.3. AssetBundleDownloadHandler

UnityWebRequestAPI允许开发人员精确指定Unity应如何处理下载的数据,并允许开发人员消除不必要的内存使用。使用UnityWebRequest下载AssetBundle的最简单方法是调用。

其需要关注的类为DownloadHandlerAssetBundle。它使用工作线程,将下载的数据流式传输到固定大小的缓冲区,然后根据下载处理程序的配置,将缓冲数据假脱机到临时存储器或AssetBundle缓存。所有这些操作都发生在本机代码中,消除了扩展托管堆的风险。此外,此下载处理程序不会保留所有下载字节的本机代码副本,从而进一步减少下载AssetBundle时的内存开销。

LZMA压缩资产绑定将在下载期间解压缩,并使用LZ4压缩进行缓存。可以通过设置来更改此行为。

下载完成后,下载处理程序的assetBundle属性提供对下载的assetBundle的访问,就像已对下载的assetBundle调用一样。

如果向UnityWebRequest对象提供了缓存信息,并且Unity的缓存中已存在请求的AssetBundle,则AssetBundle将立即可用,并且此API的操作方式与相同。

备注:在Unity 5.6之前,UnityWebRequest系统使用固定的工作线程池和内部作业系统来防止过度的并发下载。线程池的大小不可配置。在Unity 5.6中,这些保护措施已被删除,以适应更现代化的硬件,并允许更快地访问HTTP响应代码和标头。

3.4.

Unity官方在后续的支持中将会弃用这个API故而此处不做讨论。如果你是使用的5.6之前的版本可以考虑去看Unity官方的AB包使用手册的相关内容。

这个API仅适用于Unity 5.6或更早版本。

四、 总结

本文是Unity AB包系列的第一篇,仅是对Unity官方AB包手册内容的翻译以及根据自己理解内容的补充(部分内容并不是特别完善/准确,后面还会有所优化),后面的文章会逐步涉及到具体的技术问题的讨论。(Unity2020以后可以考虑使用Addressables)