前面几篇已经对打AB包需要的知识点都大概过了一遍。这篇开始开始主要就是记录自己针对一个游戏项目所做的AssetBundle的系统。代码较多,整理在项目GitHub上面。
还有几个知识点是项目中用到,前面忘记提到的,这里再整理一下:
首先,前面我们打包用到的方法都是手动去设置Asset的assetBundleName,然后调用BuildPipeline.BuildAssetBundles()方法去生成AB包,大家肯定知道这个不是一个好的方法,因为在有几百几千个Asset需要打包的时候,手动明显是很愚蠢的。所有自然而然想到的就是把需要打包的Asset放在指定的目录,然后遍历这些目录,利用脚本自动根据“文件夹/文件名”的方法自动赋值assetBundleName。类似如下代码
/// <summary>
/// 设置AssetBundleName
/// </summary>
/// <param name="fullpath">文件夹路径</param>
private static void setAssetBundleName(string fullPath) {
string[] files = System.IO.Directory.GetFiles(fullPath);
if(files == null || files.Length == 0) {
return;
}
foreach(string file in files) {
if(file.EndsWith(".meta") || file.EndsWith(".DS_Store")) {
continue;
}
AssetImporter importer = AssetImporter.GetAtPath(file);
if(importer != null) {
//文件名后缀
string ext = System.IO.Path.GetExtension(file);
string bundleName = file.Substring("Assets/Res/".Length);
//添加AB文件的后缀
if(null != ext) {
bundleName = bundleName.Replace(ext, ".ab");
} else {
bundleName += ".ab";
}
bundleName = bundleName.ToLower();
//设置AB name
importer.assetBundleName = bundleName;
//清除没使用到的AB name
EditorUtility.UnloadUnusedAssetsImmediate();
} else {
Debug.LogFormat("Set AssetName Fail, File:{0}, Msg:Importer is null", file);
}
}
}
设置好名字打好包后可以用AssetBundleEditor.ClearAssetBundleName();方法删除所有设置过的abname。
不过,我们接下来要用到的是BuildAssetBundles的另一个重载方法。
public static AssetBundleManifest BuildAssetBundles(string outputPath, AssetBundleBuild[] builds, BuildAssetBundleOptions assetBundleOptions, BuildTarget targetPlatform);
和之前的差别在于多了一个参数AssetBundleBuild[]的数组,那么AssetBundleBuild是什么用的呢,其实就是定义了一个AB包的属性,assetBundleName就是AB包的name,assetNames就是打包进这个AB包的所有Asset文件的路径。
namespace UnityEditor {
public struct AssetBundleBuild {
public string assetBundleName;
public string assetBundleVariant;
public string[] assetNames;
public string[] addressableNames;
}
}
所有我们只需要读取需要打包的文件夹,将对应的内容一一生成AssetBundleBuild,然后调用上述的接口,即可实现打包。
第二点:因为一般的工程都会有上千甚至上万的AB包,特别是美术资源,又多又大,你打一遍包会发现可能会花上你几十分钟的时间。前面文章又提到,如果不所有资源一起打包,那么依赖系统就会有问题。那么是不是我们每次需要打包,即使就修改了其中两三个文件甚至没有修改。都需要等个几十分钟才能好呢?答案当然是否定的,通过多次的尝试,只要Asset不发生改变,那么Unity就不会重新生成AB,不需要我们去做处理。(这一块希望大家自己多做尝试)
注意:经过测试发现的还有一点就是,打出的AB包对应的.manifest文件不能去删除,否则再打相关包的时候,即便Asset没有改变,还是会重新打对应的AB。但是.manifest文件又是我们实际包体不需要的,所有我们可以先将AB包打到工程外的一个指定目录,然后在将这个目录里面的AB文件全部copy到Unity里的StreamingAssets目录下。
第三点:有时候可能出现部分问题,我们需要重新全部打包,最简单的就是将所有的AB全部删掉,这样Unity就会重新打包,还有一种方法就是在打包时候的打包函数里面的参数设置为BuildAssetBundleOptions.ForceRebuildAssetBundle。
这样就会发现其实整个系统逻辑其实很简单,下面简单的梳理下我的逻辑:工程中可能需要我们打包的文件夹有三个,Res/Images里面存放较大的背景图片(单张图片对应单个AB),Res/Sprites里面存放UI图集,一个图集对应一个文件夹,一个文件夹对应一个AB,Res/Prefabs里面存放UI的prefab文件(一个prefab对应一个ab)暂时就这些文件,关于其他的Shader,Animation等文件的打包处理,以后再谈谈。整理好需要打包的Asset文件夹之后。我们只需要根据这些文件夹里面的内容,去生成对应的AssetBundleBuild,一个AB对应一个AssetBundleBuild,为其设置好assetBundleName和assetNames即可。做好上面的内容就可以成功的打包了。
接下来是关于差异包的处理:我目前的理解是,由于资源不变Unity就不会重新打包,即AB的包的MD5码不变。因为AB的name是唯一,所有每次我们打好包可以将AB的name和MD5作为一个键值对存放在同一个csv文件中。这样我们每次打包的时候读取这个csv文件,根据AB的name去查找相关的MD5,若找不到则说明是新增AB,若找到且不一样则说明是有修改的AB,找到一样就是无修改的AB。最后若csv中的name无法找到对应的AB则说明是需要删除的AB。然后我们可以用个txt文件记录资源版本号,若有修改或新增文件,版本号++。
所以,我们先利用BuildAssetBundles方法将AB包打到一个指定目录(工程内在为在Assets目录同级下的AssetBundle目录下),打包完后,如果是第一次打包,则生成对应的csv文件记录AB的MD5,和txt文件记录版本号。否则则去csv中获取上次打包的MD5码和新的MD5进行一一比较,将需要删除的AB删除,每次新增的和修改的AB提取出来到一个新的文件夹中(这些可用作热更新)。然后将所有的AB文件(不包含.manifest)文件存放到工程中的StreamingAssets目录下。
上述这些功能就是工程中目前的主要功能也是核心功能,有什么描述不对的地方欢迎指正