Unity5的AssetBundle打包方式简析

在写这篇文章前,我要先吐槽一下老版本的AssetBundle打包,其它方面我都没意见, 在打依赖包的处理上简直反人类,导致很多人宁愿资源冗余也不愿意做依赖包。不过在新版本的打包方式面前,这个问题已经不存在了。

下面是个人写的打包方法

public static class ABBuilder
{
	[MenuItem("Build/Build AB")]
	static void Build()
	{
		// 是否打包所有资源(true表示Assets文件下所有"AssetBundle"属性不为空的资源文件都会被打包)
		bool buildAll = true;
		// 这三个参数必选,其中outputPath路径必须存在
		string outputPath = Application.dataPath + "/abOutput/";
		BuildTarget platform = BuildTarget.Android;
		BuildAssetBundleOptions option = BuildAssetBundleOptions.None;
		// 这一个参数可选
		List<AssetBundleBuild> abbList = new List<AssetBundleBuild>();
		// 如果不选择打包所有资源,就必须自己指定打包内容(下面纯属示范)
		if (!buildAll)
		{
			AssetBundleBuild abb = new AssetBundleBuild();
			abb.assetBundleName = "ab1";
			abb.assetBundleVariant = "";
			abb.assetNames = new string[] { "Assets/Res/1.png", "Assets/Res/2.png" };
			abbList.Add(abb);

			abb = new AssetBundleBuild();
			abb.assetBundleName = "ab2";
			abb.assetBundleVariant = "";
			abb.assetNames = new string[] { "Assets/Res/1.prefab", "Assets/Res/2.prefab" };
			abbList.Add(abb);
		}

		// 打包
		if (buildAll)
		{
			BuildPipeline.BuildAssetBundles(outputPath, option, platform);
		}
		else
		{
			BuildPipeline.BuildAssetBundles(outputPath, abbList.ToArray(), option, platform);
		}
	}
}

这张图给出的是替资源文件设置“AssetBundle”属性的地方


如果启动上面的代码并打包完成,会在abOutput文件夹下生成6个文件(无视meta文件),分别为

abOutput

abOutput.manifest

ab1

ab1.manifest

ab2

ab2.manifest

其中ab1和ab2文件,就是我们游戏中要用到的assetbundle。那么abOutput是什么,3个manifest又是什么?

所有的manifest文件可以拖到文本编辑器里查看,明显可以看出ab1和ab2对应的manifest文件存储的是ab1和ab2的内容列表与依赖包列表,而abOutput.manifest记录的是其它assetbundle列表与每个assetbundle的依赖包列表,也就是说它是一个依赖信息总表。

而abOutput这个assetbundle文件就包含了一个资源,即总表abOutput.manifest,这个唯一资源的名字叫做“assetbundlemanifest”。

接下来所有的manifest文件都可以删除了,留下无后缀的assetbundle文件即可。

再说下如何使用这个依赖信息总表,运行游戏的时候,先载入这个总表所在的assetbundle,然后读取它的唯一资源,也就是manifest文件,保存起来,之后但凡需要载入assetbundle时,都要先检查该manifest,获得该assetbundle的依赖项,然后先载入所有被其依赖的assetbundle,再载入该assetbundle。

下面是个人写的一个简要的读取assetbundle的方法

public static class ABLoader
{
	static public string abPath = Application.dataPath + "/abOutput/";
	static public string abManifestName = "abOutput";

	static AssetBundleManifest _manifest = null;
	static Dictionary<string, AssetBundle> _abDic = new Dictionary<string, AssetBundle>();

	static public AssetBundle LoadAssetBundle(string abName)
	{
		if (_manifest == null)
		{
			AssetBundle mfab = AssetBundle.LoadFromFile(abPath + abManifestName);
			_manifest = mfab.LoadAsset("assetbundlemanifest") as AssetBundleManifest;
		}
		
		// 载入所有依赖包
		foreach (var abNameDependency in _manifest.GetAllDependencies(abName))
		{
			if (!_abDic.ContainsKey(abNameDependency))
			{
				_abDic[abNameDependency] = AssetBundle.LoadFromFile(abPath + abNameDependency);
			}
		}
		AssetBundle ab = AssetBundle.LoadFromFile(abPath + abName);
		_abDic[abName] = ab;
		return ab;
	}

	static public void UnloadAssetBundle(string abName)
	{
		if (_abDic.ContainsKey(abName))
		{
			_abDic[abName].Unload(false);
			_abDic.Remove(abName);
		}
	}

	static public void UnloadAllAssetBundles()
	{
		foreach (var ab in _abDic.Values)
		{
			ab.Unload(false);
		}
		_abDic.Clear();
	}
}


最后写下我猜测的整个内部打包流程,未经验证仅供参考:

1. 开发者调用打包函数

2. 如果是打包所有资源,即无 AssetBundleBuild[] 参数的版本,Unity就会遍历整个Assets文件夹,根据每个资源文件的“assetbundle”属性(存储在meta中),自动生成AssetBundleBuild[] 数据(纯属瞎猜)

3. Unity为每个ab创建一个manifest,写入资源列表

4. Unity依次读取每个ab的每个资源,遇到依赖资源的时候就从所有manifest中查找对应资源,一旦找到就在该manifest中记录依赖ab的名字

5. Unity逐个打包,对于每个有依赖的资源,如果依赖资源也在某个ab中,就不写入该包中,否则一同写入

6. 把所有的manifest文件的内容整合到一个与文件夹同名的manifest文件中,然后再创建一个同名ab,把该manifest写入

7. 完成打包

上面的打包或者读包的代码都是示范代码,不适合直接使用。不过了解API的基本用法,再研究下内部原理,实现功能时自然就能信手拈来。

猜你喜欢

转载自blog.csdn.net/lzdidiv/article/details/72419918