AssetBundle顾名思义翻译过来就是资源包的意思,在讲作用之前先明白为什么会存在AssetBundle的技术
为什么:假设一个游戏你已经完成了,打包成安装包的形式已经上线了,可是圣诞节到了或者其它情况,需要更新场景模型为圣诞题材或者增加新的地图,这个时候如果你是不是需要重新把新增的东西添加到游戏里面然后重新打包上线你的安装包,这对于用户来说就相当于又要重新下载一遍你的游戏,为了解决这个问题,因此就出现了AssetBundle技术,这个技术说简单点就是可以把你游戏的更新资源上传到服务器上,用户在打开游戏时,如果需要更新新资源,就直接从服务器上下载新的资源就好,就不用重新下载安装包。(最主要作用)。还可以使用AssetBundle技术减小安装包大小(把加载的资源放在服务器上)
现在来讲AssetBundle(又称AB包)的定义
定义:
- AssetBundle是一个资源压缩包(不太准确,也有可能不被压缩,后面再讲),包含模型、贴图、预制体、声音、甚至整个场景,可以在游戏运行的时候被加载;
- AssetBundle自身保存着互相的依赖关系;(比如一个模型包,一个贴图包,模型就依赖于贴图,后面会讲)
- 压缩包可以使用LZMA和LZ4压缩算法,减少包大小,更快的进行网络传输(两种算法有不同的效果,后面会讲)
- 把一些可以下载内容放在AssetBundle里面,可以减少安装包的大小(这个就相当于是作用,更新资源的作用也算这个句话的前半段,因此就统一成一个作用)
- 一个AssetBundle文件中存在两类文件serialized file 和 resource files,像游戏里面的模型,预制件就会被变为serialized file储存在AssetBundle文件中,游戏中的二进制资源(图片、声音)就会以 resource files形式保存在AssetBundle文件中
这里附上一张AssetBundle和服务器的传输关系图,AssetBundle包下载之后是会保存在本地的
现在我们来看如何对游戏中的资源文件进行打包操作
创建:
1.创建想要打包资源所属的包的路径(包名和后缀),这里名字如果用/,就会又层级关系,如ui/menue,该资源包就会出现在ui文件(没有会自动创建)下中的名为menue包中
2.创建一个editor脚本(编辑器扩展脚本)
using System.IO;
using UnityEditor;
public class CreatAssetBundles //扩展器脚本,用来创建AB包的
{
[MenuItem("Assets/Build AssetBundles")]//通过编辑器调用函数
static void BuildeAllAssetBundle()//因为unity在打ab包时,是不用指定打哪个ab包的,它会把所有符合的ab包都进行打包
{
//一般来说是使用三个下面这个重载
//outputPath就是指打的ab包在哪个路径
//BuildAssetBundleOptions为打包方式(压缩的方式)(后面会讲)
// BuildTarget是打包的平台(如苹果,安卓,windows)
string dir = "AssetBundles";//因为如果没有该文件,BuildAssetBundles不会自动创建,所以要做一个判断
if(Directory.Exists(dir)==false)//不存在该文件就创建该文件
{
Directory.CreateDirectory(dir);
}
//这就是打包的代码
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
}
}
效果演示(注意:如果两个不同的资源,指定同一个包,也会将两个资源打包在同一个包中)
加载:
创建一个脚本,首先将本地的AB包中载到内存中,随后通过名字加载指定AB包中的资源,最后使用该资源。(这里同Resourses文件加载有点相似)
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoadFromLocalFile : MonoBehaviour
{
// 从本地ab包加载到游戏
void Start()
{
AssetBundle ab= AssetBundle.LoadFromFile("AssetBundles/cube.ab");//这是要加载的包的包名,注意需要带上后缀,还有加载本地ab包是相对路径
GameObject cube= ab.LoadAsset<GameObject>("Cube");//这是AB包中的资源名,这个Cube是AB包中的资源
Instantiate(cube);
}
}
演示效果
在写加载代码时会发现,AssetBundle的静态函数有这么多,具体详情见用户手册
加载包:
AssetBundle.LoadFromMemory()是通过读取加载AB包的字节文件获取AB包(字节文件一般是从web上获取)
AssetBundle.LoadFromFile()是加载本地的AB包文件
还可以用 UnityWebRequest.GetAssetBundle()方式从web上加载AB包
参考
AB包的加载大概就这三类用得多,其中通过UnityWebRequestAssetBundle用得
从AB包对象中加载资源:
AssetBundle(对象).LoadAsset(assetName)
AssetBundle(对象).LoadAssetAsync() 异步加载,加载较大资源的时候
AssetBundle(对象).LoadAllAssets() 加载AB包中所有的对象,不包含依赖的包
AssetBundle(对象).LoadAllAssetsAsync() 异步加载全部资源
注意知识点:
AB包的分组策略:在给不同的资源赋不同的包名的时候就是相当于在分组,因此可以参考一下一些常规的分组策略(仅作参考)
- 按照逻辑实体打包:如UI界面,人物角色,场景所共享资源(后面会将的依赖)分别一个包
- 按照资源类型:声音,模型,材质分别打一个包
- 按照使用情况:如在第一个关卡中用到的资源,第二个关卡中用到的资源分别打包
以下还有一些可以参考的分组策略:
- 经常更新的资源放在一个单独的包里面,跟不经常更新的包分离
- 把需要同时加载的资源放在一个包里面
- 可以把其他包共享的资源放在一个单独的包里面(包的依赖)
- 如果对于一个同一个资源有两个版本,可以考虑通过后缀来区分
包的依赖:前面多次提到了包的依赖,那么包的依赖是什么,有什么作用这里就来讲解下,假设我现在有个材质球A(其中包含贴图等),有模型B和模型C用到了材质球A,这时如果将模型B和模型C分别打包时,就会有两个包,这两个包里面有相同的材质球A的资源,就会造成不必要的浪费,因此就可以单独将材质球A进行打包,这时再对模型B和模型C进行打包时就会发现模型B,C的包都比较小了(因为没有了材质球的资源),这里的依赖关系是unity自动进行关联的
注意:由依赖关系时,在加载AB包时,一定要加载对应的依赖的AB包,不然会导致AB包的不完整(建议先加载依赖的AB包再加载AB包)
压缩方式(BuildAssetBundleOptions):
可以参考文档Unity - Scripting API: BuildAssetBundleOptions (unity3d.com)
这里说明一下常用的
BuildAssetBundleOptions.None:unity系统默认使用LZMA算法压缩,该算法的优点是压缩包小,缺点是加载速度慢。因此在第一次加载LZMA算法压缩包时,可能会比较慢,但是该包一旦被加载一次后,系统自动讲该压缩包转化为LZ4压缩算法包,并保存在本地上
BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,优点:加载快,缺点:包大
BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4压缩,优点加载指定资源而不用解压全部,缺点压缩率没有LZMA高
下面两者比较的话这里推荐使用lz4压缩算法,因为速度上和不压缩相差不大,但是包比不压缩的小
Manifest文件作用:Manifest相当于一个信息表,这里的Manifest文件有两种,一种是创建AB包时会自动创建一个AssetBundles.manifest这个文件,这个文件相当于一个总览信息表,里面包含了所有AB包的信息;还有就是AB包的manifest,其中就包括,该AB包中的资源信息,包括依赖关系CRC(完整校验码)等。打个比方AB包就相当于班,学生相当于资源,AssetBundles.manifest就相当于是一个年级表,其中包含了各个班的信息,而AB包的manifest就相当于班级表,其中每个班包含了具体的学生,依赖就相当于是老师(一个老师可以教多个班,也可以由多个老师教)(不完全正确,只是举个例子方便理解)
可以通过AB包来获取对应的依赖(通过manifest),其实就可以看成manifest也属于AB包的一种资源
AssetBundle manifestAB = AssetBundle.LoadFromFile("AssetBundles/AssetBundles");//这
//一步是加载AB包
加载AB包
AssetBundleManifest manifest = manifestAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");//这一步就相当于获取AB包中的Manifest资源
string[] strs = manifest.GetAllDependencies("cube");//这一步就是获取指定资源cueb的依赖包,随后就可以都把该依赖包加载出来即可
AB包的卸载:
AssetBundle(对象).Unload(true)//卸载该包内的所有资源
AssetBundle(对象).Unload(false)//卸载该包内没有被使用的资源,没有被卸载的资源会一 //直保留(即使后面没有被别的物体所使用了)
个别资源的卸载
Resources.UnloadUnusedAssets //卸载所有没有被场景引用的资源对象
Reources.UnloadAsset(Object) //释放AssetBundle文件内存镜像同时销毁所有已经Load的
//Assets内存对象
AB包的浏览和构建工具: