zoukankan      html  css  js  c++  java
  • 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)

    1. 头部保存了版本号、数据类型、文件信息、是否压缩等这些描述信息。

      文件信息记录了数据部分里面的所有单个资源的文件名以及在整个AssetBundle中

      文件offset和size,通过这个信息可以直接获取到AssetBundle中的某一个文件的数据。

      从Unity5.3版本开始这部分数据会单独生成一个AssetBundle同名的.manifest文件。

    2. 数据块保存了在build AssetBundle时候标记的textures、prefabs、materials等资源文件

    文件头结构(未压缩):文件字节流以''结尾,且为大端方式储存。

     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包,要在BuildAssetBundleassetBundleOption

    |BuildAssetBundleOptions.DeterministicAssetBundle来确保文件的唯一性。

    Manifest文件:

    Manifest文件仅用于检查增量构建,运行时不需要。因此不需要打包进正式发行的游戏中。

    每个AssetBundle都有一个manifest文件,包含如下信息:

    1. CRC(循环冗余码):资源文件的哈希码,在该AssetBundle中的所有资源有一个单一的哈希码,用于检查增量的构建。
    2. Type tree哈希码:在该AssetBundle中所有类型有一个单一的哈希码,用于检查增量的构建。
    3. Class types:该AssetBundle中所有的类类型。当为type tree做增量构建检查时将产生一个新的哈希码。
    4. 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

    参考:

  • 相关阅读:
    【算法】HashMap相关要点记录
    【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)
    SpringCloud Openfeign Get请求服务传递对象的报400 Post not support的错误解决办法
    掌握 Promise 的逻辑方法
    JavaScript的执行上下文,真没你想的那么难
    一套标准的ASP.NET Core容器化应用日志收集分析方案
    在IIS中部署前后端应用,多么痛的领悟!
    吐槽一下Abp的用户和租户管理模块
    ant-design-vue中tree增删改
    微服务下的注册中心如何选择
  • 原文地址:https://www.cnblogs.com/pinkfloyd/p/6489979.html
Copyright © 2011-2022 走看看