压缩格式
常用的压缩格式为两种:
- LZMA:单包一次全部读入内存,压缩率高,文件小。
- LZ4:可以分块读入内存,压缩率相比LZMA低,文件相比LZMA更大。
LZMA的特性导致包体不能过大,共享资源在生成时会生成场景文件:level0、level1,资源文件sharedassets0.assets等。
LZ4的特性则对包体大小更宽松,可以直接读取对应资源而不用整包读取,共享资源生成时会直接生成单包文件data.unity3d
资源打包
在Unity3d中,需要打包的资源可以分为三类:
- 最普通的通用共享资源
- Resource文件夹内资源
- 作为AssetBundle打包的资源
放在非特殊文件夹下的资源,如果没有被引用到,那么就不会被打包出去(如一个Prefab引用了这个资源,而这个Prefab被放置在了场景中,这个场景又被放入了Scene In Build中,那么它们都是被引用资源,才会被打包。)
Resource文件夹的功能是允许用户动态的加载资源,这就导致引擎并不知道一个资源到底会不会被引用,所以该文件夹中的全部内容都会被打包出去。并且Resource文件如果过多,则会增加编译时序列化的运行时间,增加启动时索引算法的构建,造成程序启动时卡顿未响应等问题,现应尽量避免在该文件夹内储存美术资产,推荐只存些配置文件之类的。
AssetBundle是目前unity官方对于资源分发的唯一解,个人觉得就是个半成品,极其难用,在unity5.x之后的版本稍有改善,但还远不足以成为一个现代的资源分发解决方案。你需要用很多的精力去处理资源的依赖关系,包体粒度,内存管理以及资源重复等问题。
ab包的名字允许使用/,同时在打包后的目录中会以文件夹的形式储存。如一个资源的ab名为res/a.pck,那么打包后的资源a.pck就在res文件夹中。我们可以编写一个工具,将所有ab包名,资产名,guid,所在包名等信息储存到一张表中,并将该表也标记为ab包,在启动时读取该表建立索引,然后你就可以使用哈希之类的算法去使用路径找到资源所在ab包和具体位置。
ab包多数情况也不会只打一个包,用户可以自行控制每个包的大小,所以可以直接使用LZMA压缩算法。
个人推荐的AssetBundle打包流程:
在Assets目录中新建如resdb作为一个用来打ab包的文件夹,所有资源都不能与resdb外的任何资源发生引用关系,如果和resdb外的资源发生引用,那么该资源可能会被打包进共享资源包或者Resource资源包造成资源重复打包,只可以在该文件夹内部的资源互相引用。在带有热更的商业手游开发的大多数情况下,都是使用Xml、Sqlite等作为本地数据库和配置文件储存资源引用关系,在运行时读取资源名,然后再去动态查找在绑定引用,如此记录:
Id: int | Desc: string | Img: string |
1001 | 恢复药水 | resdb/potion.png |
在需要的时候,再去使用字符串去动态查找该资源。这么做的原因是,如果该资源改动,那么与此资源相依赖的其他资源可能收到牵连。
打包后的ab包文件夹推荐放在游戏的根目录下,也就是和exe同目录,或者放在StreamingAssets中。
既可以嵌入打包也可以AB打包的方式
在resdb中储存所有资源并标记好ab名后,编写了一个工具,该工具可以扫描以resdb/开头的ab包,然后建立一个带有实际资产对象引用的本地映射表,储存在Resources文件夹中,这样resdb的所有资源都会在Resources文件夹中有一个引用,在打包项目时resdb也就和普通资源一样被打包为通用共享资源,而不是Resources资源,因为Resources文件夹内只是储存的资产引用和索引等信息,用来支持用户使用路径索引对象,资源并不实际存在Resources文件夹内,所以不会造成增加编译时间以及启动卡顿未响应等问题。
当项目越来越大时,发布可以切换成AB打包方式,将之前在Resources文件夹内生成的本地索引表删除,这样resdb就会断开引用,不会被打包到共享资源,造成与AB包内资源重复。