Unity的资源管理是一个比较复杂的模块,如果管理不好,可能导致最终包体大小偏大,程序运行时候内存居高不下,因此了解并掌握Unity的资源管理显得特别重要。
Unity中资源一般存放在两个目录下,一个是Resource目录,另一个是StreamingAsset目录。放在Resource目录下的资源在打包的时候会被压缩并打包到安装包中(assets资源),只读,而在StreamingAsset目录下的资源在打包的时候不会被压缩但会被打包到安装包中(assetbundle资源),安装时候会被解压到相应平台的对应目录(通过Application.streamingAssetsPath可获得解压路径)下,只读。因此,可以根据Unity的资源格式,把Unity资源管理分为两种,一种是Resource模式,另外一种是AssetBundle模式。
- Resource文件夹中的所有Asset和Object都会合并到同一个序列化文件中,这个序列化文件中还含有元数据(Metadata)和索引(Indexing)信息,类似于AssetBundle。该文件夹下单个资源有一个2G的大小限制,Unity官方不推荐把资源放在该目录。
- AssetBundle是一个使用LZMA或LZ4(Untiy5.x新增)压缩算法压缩的包含特定平台资源(如模型、贴图、预设、音效、场景等)的资源集合,可以在游戏运行时候被加载到游戏中,AssetBundle相互之间可以存在依赖关系。
Untiy官方推荐使用AssetBundle模式对资源进行管理,接下来通过三方面来了解AssetBundle的运行机制。
1.AssetBundle的内部结构:
我们说AssetBundle的时候一般是指两个东西,一个是指存储在磁盘上的资源,它由两部分组成:序列化文件和资源文件,序列化文件是由该资源集合下的资源文件拆分写入到一个文件得到,资源文件是可以高效加载的二进制存储的资源(如贴图、音效);另一个是指通过代码加载到内存的对象,它包含一张拥有你打包进来的全部资源的地址和当某个资源被加载时需要依赖加载的资源的Map(如加载模型时依赖的贴图和网格路径)。
普通的AssetBundle内部结构如下图:
场景AssetBundle内部结构如下图:
由于Unity并不是开源引擎,而且官方也未给出太多有关AssetBundle的内部实现细节,因此我们并无法知道关于AssetBundle具体的格式,不过云风早前对其内部格式有过一次探究,具体可参考该文章。
2.AssetBundle的打包流程:
AssetBundle打包涉及到一个打包策略的问题,打包策略会直接影响到打包后资源的数量和大小,从而影响到资源加载的效率(IO、解压、申请内存)和热更资源包的大小。因此我们需要对包的大小和数量做一个平衡,根据项目具体情况整理出一个合适的打包策略。常用的打包策略有如下几种:
- 按照逻辑实体分组:如一个UI界面或者所有UI界面一个ab,一个角色(模型和动画)或者所有角色一个ab,所有场景共享部分一个ab等;
- 按照类型分组:如所有音效资源一个ab,所有shader一个ab,所有模型一个ab,所有材质一个ab等;
- 按照关联性分组:按照某一时间有关联的资源,如不同游戏场景(如登陆,主场景,战斗中)或者不同关卡将所有资源(角色、贴图、音效等)打包成一个ab。
- 按照使用频率分组:如可以将经常使用或者常驻内存的公共资源打包成一个ab,其他依次按照使用频率打包成一个ab;
- 按照加载顺序分组:如可以将需要同时加载的可以打包到一个ab中;
- 按照依赖关系分组:如有两个模型使用的都是同一个材质和贴图,那么模型和材质贴图之间就是依赖关系。如果我们这两个模型都单独打包出来那么就会打包出两份材质和贴图,这样包就会变大;如果我们把所依赖的材质和贴图单独打包到一个ab,然后再分别打包两个需要依赖这个材质和贴图的模型,那么就只会打包一份材质贴图,这种方式叫做依赖打包。不过前一种打包方式在加载模型的时候无需注意什么,后一种打包方式在加载模型的时候就要提前加载依赖包,这里就是指材质贴图ab,否则会出现材质贴图丢失情况。
在实际项目中,可以将上述几种策略交互使用,对应具体的应用需求来灵活的采用分组策略。
Unity5.0之前版本,Unity通过编译管线BuildPipeline提供了三个方法来创建AssetBundle文件:
// 将任意类型的Assets打包成一个AssetBundle,适用于对单个大规模场景的细分
// 第一个是主资源,第二个是资源数组,这两个参数必须有一个不为null,如果主资源存在于资源数组中,是没有任何关系的,如果设置了主资源,