Unity AssetBundle 解析 (一)——AB包介绍与构建
介绍
AssetBundle是Unity提供的一种资源存储压缩包,包含了一些Unity的资源,如图片,模型,纹理,音视频等等,也可以包含用户自定义的二进制文件。提供了一种程序运行时动态加载资源包的方法,如游戏中不同版本的资源更新即通过更新资源AB包即可。
Unity官方文档描述AssetBundle为一个容器,就像文件夹一样,包含其他文件。这些其他文件有两种类型,一种是序列化文件,包含资产分解成单独的对象并写到该单个文件中。(o((⊙﹏⊙))o,在说什么);另一种是资源文件,是为某些资产(纹理和音频)单独存储的二进制数据块,以允许Unity从另一个线程上的磁盘有效加载它们。
总而言之,AssetBundle就是一种资源包,让你能在运行时动态的加载卸载它,也可以灵活更新,即可以无需放在游戏本地,大大降低了包体大小。
NOTEUnity提供了两种加载资源的方法(2018.2之后又新增一种)
- 通过放在本地特殊文件夹Resources下直接加载资源,通过Resource.Load()这个API加载就行,简单粗暴,非常适合新手,可以不用管资源怎么管理,加载就完事了。但有缺点,不利于拓展,也无法分平台发包以及从线上下载等。
- 本文要介绍的AB包方式,应该是目前比较通用的方案
- 2018.2版本后新增的可寻址方案Addressable,这个后续再写个博客
其实无论是用Resources或者Addressable加载方式,底层都是基于AssetBundle实现的,只是Unity引擎会自动做这步操作
官方文档:https://docs.unity3d.com/Manual/AssetBundlesIntro.html
1. 工作流
- 设置资源的BundleName以及BundleVariant
- 打包AssetBundle
- (将AB包上传到服务端,若放在本地则可跳过)
- 加载AssetBundle
- 实例化AssetBundle中的资源
- AssetBundle的卸载
2. 打包AB包
设置资源的AssetBundleName和Variant
AssetBundleName与Variant介绍
将要打包成AssetBundle的资源设置AB包名字和变体名字
- AssetBundleName: 即是该资源要打包进哪个AB包里面,若多个资源的AssetBundleName相同(且变体名Variant也相同)则这些资源会被打包到同一个AB包里,换言之,这个AB包包含了多个资源。
AssetBundleName不能包含”.”这个特殊字符,会与variant产生冲突
使用/分隔符可以按层级建AB包,如AssetBundleName为A/B/C的AB包会被打包成A文件夹下的B文件夹下的名字为C的AB包
- Variant: 类似后缀名的东西,称为变体,默认为.assetbundle,一般后缀大家都设置为.assetbunlde和.unity3d,那有人会用,这个有啥用。主要的用途应该用来做版本控制,即同种资源的不同形态下的加载,如不同语言包的同类型资源的加载,如可命名资源a的不同变体为a.cn,a.en等,根据不同的语言包加载不同的AB包。
参考链接:https://dev.twsiyuan.com/2017/05/unity-assetbundles-variants.html
AB包的分组方式
将哪些资源打包到一个AB包下是让人头疼的事情,就像你要把你的衣服放进衣柜里,总而言之就是整理的过程,而这种事情越早确定越好,到后期再更改就显得很麻烦。
Unity官方推荐的分类方式有以下几种:混合搭配才是王道
- 按逻辑进行分类即按该资源被哪个功能应用到,同种功能用到的资源打到一个AB包内
- 如将一个UI界面的所有图片纹理,布局文件打包到一个AB包
- 将一个角色的所有动画,模型打包到一个AB包
- 将一些公用的资源打包到一个AB包
- 按类型分类将同一个类型下的资源打包到一个AB包中
- 如将Windows和Mac平台下使用到的不同Shader资源分类打包
- 将不同版本的Unity AB包进行分类打包
- 同时使用的打包即考虑到哪些AB包可能会在某个时间段一起使用,则打包到同一个AB包中
- 如某关卡下需要加载的所有资源一起打包
- 某个场景下需要加载的所有资源,你应该尽可能将所有AB包依赖项都打包进来,避免加载其他依赖项以下是官方给出的一些分类建议:
- 将经常更新和很少更改的资源分开打包
- 将基本会同时使用的资源打包,如模型和它用到的纹理,动画一起打包
- 将一些很多资源会引用到的公共资源分开打包,避免重复打包
- 如果两组几乎不可能同时加载的资源分开打,如Standard和High解析度资源分开打
- 如果一次加载某个AB包经常用到少于50%其中的资源,则应该分开打包
- 考虑合并比较小但经常一起使用的AB包
- 如果某组资源只是同个资源的不同版本分类,使用variant打包(如不同语言版本的同个资源)
设置的方式
无非两种方案,一种手动,一种写脚本按一定规则变成自动
- 手动方案,在资源的Inspector栏目手动添加/设置AssetBundleName和Variant
- 一键自动设置AssetBundleName和Variant可设置规则,如按文件夹进行AB包的命名,同个文件夹下的资源打包到同一个AB包设置AB包AssetBundleName和Variant的API如下:
AssetImporter.GetAtPath(filePath).SetAssetBundleNameAndVariant(bundleName, bundleVariant);
Build AssetBundle
前期准备做好了之后,这里就比较简单了,调用Unity的打包API就OK了,不过这里需要注意打AB包的一些参数设置,不同平台,不同的压缩方式等。//方式1. Unity会遍历所有设置过AssetBundleName及Variant的文件进行全局打包 BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, BuildAssetBundleOptions.None, BuildTarget.StandaloneOSX); //方式2. 通过一个BuildMap打包指定的文件 AssetBundleBuild[] buildMap = new AssetBundleBuild[2]; buildMap[0].assetBundleName = "enemybundle"; string[] enemyAssets = new string[2]; enemyAssets[0] = "Assets/Textures/char_enemy_alienShip.jpg"; enemyAssets[1] = "Assets/Textures/char_enemy_alienShip-damaged.jpg"; buildMap[0].assetNames = enemyAssets; buildMap[1].assetBundleName = "herobundle"; string[] heroAssets = new string[1]; heroAssets[0] = "char_hero_beanMan"; buildMap[1].assetNames = heroAssets; BuildPipeline.BuildAssetBundles("Assets/ABs", buildMap, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);压缩方式
- BuildAssetBundleOptions.None使用LZMA格式压缩,这是序列化数据文件的单个压缩LZMA流(官方翻译的),LZMA压缩要求在使用整个AB包之前先对其进行解压缩。所以这种压缩方式的AB包体最小,但同时加载时间最长(永远的定律)。在第一次加载解压完成后,Unity会对他进行重新压缩为LZ4的格式,这种格式无需解压整个AB包即可读取里面的资源。这种方式一般使用在当加载某个AB包时就会使用到其里面所有的资源。如某个角色的AB包,里面的模型,动画,纹理等都会被同时需要,这种方案就比较适合。
通过UnityWebRequestAssetBundle加载的LZMA格式AB包将自动重新压缩为LZ4压缩,并缓存在本地文件系统上。而通过自己写的下载方案,则可以调用AssetBundle.RecompressAssetBundleAsync API重新压缩
- BuildAssetBundleOptions.UncompressedAssetBundle这种方式的AB包完全没有压缩,所以包体是最大的,但同时加载的时候就比较快
- BuildAssetBundleOptions.ChunkBasedCompression使用LZ4格式压缩,这种方式的包体比LZMA格式大,但无需解压完整个AB包就能读取里面的数据(LAMA就需要),LZ4使用基于块的算法,该算法允许AssetBundle分段或“块”加载。解压某些块就能使这些块中包含的资源先被读取,即使其他块的资源还未被解压。
NOTE
使用ChunkBasedCompression具有与BuildAssetBundleOptions.UncompressedAssetBundle差不多的加载时间,却具有减小磁盘大小的额外好处。
BuildTarget
打包平台,即要打包哪个平台的AB包,如Android平台则使用BuildTarget.Android,不同平台使用的AB包不同,无法跨平台使用。
输出文件夹
一般本地的AB包放在Unity特殊目录StreamingAssets下,通过Application.streamingassetpath读取
和Resources特殊目录一样,Unity会将该目录下的所有文件都打包,故需要特别注意,在打包不同平台的应用程序时,避免将其他平台的AB包一起打包,否则将大大增加包体大小。如Android的apk包就不应该确保StreamingAssets目录下只有Android平台的资源,其他平台的资源都是没用的,只会增加包体大小。
3. 生成的AB包文件
无论用何种方式打包AB包,最终生成的文件结构都是相同的。在同一输出文件夹下,生成的文件数目为该文件夹下 AB包数目*2+2,即假设有2个AB包打包输出到同一文件夹下,则在该文件夹下会生成6个文件。
每个AB包都会生成一份AB包本体文件(如上图的cube.a,若没有设置AB包Variant,则默认AB包后缀为.assetbundle),一份AB包的清单文件(上图的cube.a.manifest);
另外文件夹下会生成文件夹同名的两个文件,上图的StreamingAssets及StreamingAssets.manifest。StreamingAssets文件没有意义,StreamingAssets.manifest记录了一份该文件夹下AB包清单。
AB包的manifest则记录了AB包的一些依赖关系以及包含的资源清单,该清单仅用于查看,没有其他意义。
故构建AB包后真正有用的文件就是生成的AB包本体。
暂无关于此日志的评论。