AssetBundle文件结构浅析
压缩方式:
AssetBundle压缩后大致文件组成:
LZMA:
压缩数据的头有13个字节,前5个字节是lzma解压缩的API需要插入的props,
接下来的4字节是解压缩后的数据库长度。使用时需要整体解包,优点是打包后体积小,
缺点是解包时间长,导致加载时间长。
LZ4格式:
相较LZMA会生成更大的压缩文件,但优点是使用时不需要整体解压。
LZ4是一种基于chunk的算法,加载对象时只有相应的chunk会被解压。
该格式需要Unity5.3以上的版本,以前的版本并不支持。
不同压缩方式使用加载API对比:
API |
未压缩 |
LZ4(块压缩) Unity5.3后 |
LZMA(流压缩) 默认压缩方式 |
WWW* |
内存:未压缩 无需额外的处理 |
内存:LZ4HC压缩 无需额外处理 |
内存:LZ4压缩 LZMA解压+下载过程中LZ4压缩 |
LoadFromCacheOrDownload |
内存:无需额外内存 需要从硬盘读取 |
内存:无需额外内存 需要从硬盘读取 |
内存:无需额外内存 需要从硬盘读取 |
LoadFromMemory(Async) |
内存:未压缩 无需额外的处理 |
内存:LZ4HC压缩 无需额外处理 |
内存:LZ4压缩 LZMA解压+LZ4压缩 |
LoadFromFile(Async) |
内存:未压缩 无需额外的处理 |
内存:无需额外内存 需要从硬盘读取 |
内存:LZ4压缩 LZMA解压+LZ4压缩+从硬盘读取 |
WebRequest (同时支持Caching) |
内存:未压缩 一般无需额外处理, 读Cache需读取硬盘 |
内存:LZ4HC压缩 一般无需额外处理, 读Cache需读取硬盘 |
内存:LZ4压缩 LZMA解压+下载过程LZ4压缩,读Cache需读取硬盘 |
AssetBundle文件结构:
通常情况下AssetBundle文件组成:
场景打包AssetBundle组成结构
AssetBundle由两部分组成:头部(header)数据部分(data segment)
-
头部保存了版本号、数据类型、文件信息、是否压缩等这些描述信息。
文件信息记录了数据部分里面的所有单个资源的文件名以及在整个AssetBundle中
文件offset和size,通过这个信息可以直接获取到AssetBundle中的某一个文件的数据。
从Unity5.3版本开始这部分数据会单独生成一个AssetBundle同名的.manifest文件。
-
数据块保存了在build AssetBundle时候标记的textures、prefabs、materials等资源文件
文件头结构(未压缩):文件字节流以'\0'结尾,且为大端方式储存。
1 // AssetBundle文件头结构 2 struct AssetBundleFileHead { 3 struct LevelInfo { 4 unsigned int PackSize; 5 unsigned int UncompressedSize; 6 }; 7 8 string FileID; 9 unsigned int Version; // bundle格式版本 3.5~4.x : Version == 3 10 string MainVersion; // 11 string BuildVersion; 12 size_t MinimumStreamedBytes; 13 size_t HeaderSize; 14 size_t NumberOfLevelsToDownloadBeforeStreaming; 15 size_t LevelCount; 16 LevelInfo LevelList[]; 17 size_t CompleteFileSize; 18 size_t FileInfoHeaderSize; 19 bool Compressed; 20 };
一个 AssetBundle是由多个asset 文件打包而成,顺序打包了这些 asset 。
序列化成这样的结构:
1 // asset 文件头结构 2 struct AssetFileHeader { 3 struct AssetFileInfo { 4 string name; 5 size_t offset; //除去 HeaderSize 后的偏移量 6 size_t length; 7 }; 8 size_t FileCount; 9 AssetFileInfo File[]; 10 };
分解出被打包在一起的多个 Asset 了,offset 表示的是除去 HeaderSize 后的偏移量。
可以用 HeaderSize 加上那个部分的 offset 得到这个部分相对于整个 bundle 的文件偏移。
对于每个 asset ,又有它自己的数据头。数据头除了基本的数据头结构 AssetHeader 外,
还有额外的三个部分:
1 // asset数据头 2 struct AssetHeader { 3 size_t TypeTreeSize; // TypeTree对AssetBundle是可选的 4 size_t FileSize; 5 unsigned int Format; // 根据Unity版本决定 6 size_t dataOffset; 7 size_t Unknown; 8 };
在发布到移动设备上时,TypeTree 是不打包到 asset bundle 中的。
每个 asset 对象,都有一个 Class ID ,可以在 TypeTree 中查到如何反序列化。
Class ID的和具体类型的对应关系,在 Unity3d 的官方文档 可以查到。
接下来是每个 AssetObject 的描述数据。
1 // Object 数据头 2 struct ObjectHeader { 3 struct ObjectInfo { 4 // int为"小端编码"的4字节整数 5 int offset; 6 int length; 7 int pathID; 8 byte classID[8]; 9 }; 10 int ObjectCount; 11 ObjectInfo Object[]; 12 };
如果想取得正确的相对整个文件的位置:
应该是文件的 HeaderSize + asset.offset + asset.dataOffset + object.Offset 。
接在 ObjectHeader 后的是 AssetRef 表,记录了 Asset 的引用关系。
用于指明这个 bundle 内 asset 对外部 asset 的引用情况。
AssetRefTable 结构如下:
1 struct AssetTable { 2 struct AssetRef { 3 byte GUID[16]; 4 int type; 5 string filePath; 6 string assetPath; 7 }; 8 int Count; 9 byte Unknown; 10 vector Refs; 11 };
每次同样的asset,打包后的包大小都不一致是因为生成AssetBundle的时候用了lzma压缩,
只要压缩前的数据有一点点不一样,那lzma 后的结果就几乎完全不一样。
想要得到一模一样的AssetBundle包,要在BuildAssetBundle的assetBundleOption
里|BuildAssetBundleOptions.DeterministicAssetBundle来确保文件的唯一性。
Manifest文件:
Manifest文件仅用于检查增量构建,运行时不需要。因此不需要打包进正式发行的游戏中。
每个AssetBundle都有一个manifest文件,包含如下信息:
-
CRC(循环冗余码):资源文件的哈希码,在该AssetBundle中的所有资源有一个单一的哈希码,用于检查增量的构建。
-
Type tree哈希码:在该AssetBundle中所有类型有一个单一的哈希码,用于检查增量的构建。
-
Class types:该AssetBundle中所有的类类型。当为type tree做增量构建检查时将产生一个新的哈希码。
-
Asset names:该AssetBundle中所有明确包含的资源名字,依赖于该AssetBundle的其他AssetBundle。
不同格式文件的打包类型:
文件格式 |
支持后缀名 |
打包后的类型 |
文本 |
txt xml |
TextAsset保存在TextAsset的text属性中 |
二进制 |
bytes |
数据保存在TextAsset的bytes属性中 |
Prefab |
prefab |
GameObject 需instantiate实例化才可以使用 |
FBX |
fbx |
添加了Animator为 GameObject,需Instantiate实例化 |
Mecanim |
N/A |
GameObject Mecanim中必须制作为预制件进行加载 |
Legacy动画 |
N/A |
同上 |
图片 |
bmp jpg png |
Texture2D Sprite |
音乐 |
aiff mp3 ogg |
Audio Clip |
ScriptableObject |
asset |
Scriptable Object派生类 |
Unity Studio输出格式:
Unity Studio和Disunity解包工具基本都是首先处理AssetBundle包,读入stream中decode。
首先检测签名,查看AssetBundle打包的平台,根据不同平台来选择不同的加载方式。
然后根据偏移和数据ID来解析stream,并将数据转为相关的文件格式。
资源类型 |
输出格式 |
图片 |
pvr 和dds |
音频 |
mp3 |
网格 |
txt |
Shader |
shader |
配置表 |
txt |