首先,发下参考链接,阿赵的博客,写得很好:
阿赵文中写过:
“先来说说最多人关心的问题,Unity自己处理依赖关系。
实际上来说,所有需要打包成AssetBundle的资源,你是要先赋予它一个assetBundleName的,在它有了assetBundleName之后,实际上它的信息已经存在于AssetDataBase里面了。所以在打包的时候,只需要调用BuildPipeline.BuildAssetBundles方法,它会把记录了的在AssetDataBase里面的所有资源先计算出依赖关系,再拆分打包。这个步骤是一点问题都没有的。要注意的是,你所有依赖的资源都必须赋予assetBundleName,不然,依赖就不会被拆分。”
实际上来说,所有需要打包成AssetBundle的资源,你是要先赋予它一个assetBundleName的,在它有了assetBundleName之后,实际上它的信息已经存在于AssetDataBase里面了。所以在打包的时候,只需要调用BuildPipeline.BuildAssetBundles方法,它会把记录了的在AssetDataBase里面的所有资源先计算出依赖关系,再拆分打包。这个步骤是一点问题都没有的。要注意的是,你所有依赖的资源都必须赋予assetBundleName,不然,依赖就不会被拆分。”
我很好奇,使用Unity5,这样打包出来的AssetBundle究竟是什么样。于是参照他的方式,做了下实验。
仍然是4张贴图,名字分别是T1,T2,T3,T4。2个材质球,M1,使用贴图T1,T2,T3。材质球M2,使用贴图T4。2个Cube物体,obj1和obj2,分别使用材质M1和M2.将obj1和obj2做成prefab。
打包的代码很简单:
using UnityEngine;
using UnityEditor;
public class BuildAssetBundle : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
[MenuItem("AssetBundle/BuildAssetBundle")]
public static void CreateAssetBundle()
{
BuildPipeline.BuildAssetBundles("Assets/Output", BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
}
}
1.只打包prefab
点击obj1,new一个AssetBundle name为“obj1”,同样为obj2 new 一个AssetBundle name为“obj2”,分别设置好。使用上面代码生成的菜单,导出AssetBundle。这样打包一共3个AssetBundle文件,分别是obj1,obj2,还有一个跟导出路径同名的output。其余的manifest文件是本地使用,暂时不关注。
使用如下加载代码测试:
using UnityEngine;
using System.Collections;
public class ImportAssetBundle : MonoBehaviour {
// Use this for initialization
void Start () {
AssetBundle ab1 = AssetBundle.LoadFromFile("Assets/AssetBundle/obj1");
if(ab1 != null)
{
Debug.Log("ab1 create success!");
Instantiate(ab1.LoadAsset("oBj1"));
//Object M1 = ab1.LoadAsset("M1");
//Debug.Log(M1.name);
string[] names = ab1.GetAllAssetNames();
foreach (var elem in names)
Debug.Log("Asset name is " + elem);
Object[] objs = ab1.LoadAllAssets();
foreach (var elem in objs)
Debug.Log("Obj is " + elem.name);
Object[] objsWithSub = ab1.LoadAssetWithSubAssets("Obj1");
foreach (var elem in objsWithSub)
Debug.Log("Obj with sub assets is " + elem.name);
}
AssetBundle ab1Manifest = AssetBundle.LoadFromFile("Assets/AssetBundle/Output");
if (ab1Manifest != null)
{
Debug.Log("ab1Manifest create success!");
string[] names = ab1Manifest.GetAllAssetNames();
foreach (var elem in names)
Debug.Log("asset name is " + elem);
AssetBundleManifest manifest = ab1Manifest.LoadAsset("AssetBundleManifest") as AssetBundleManifest;
if(manifest != null)
{
Debug.Log("load manifest success");
string[] AllAB = manifest.GetAllAssetBundles();
foreach (var elem in AllAB)
Debug.Log("AssetBundle is " + elem);
string[] AllDependences = manifest.GetAllDependencies("obj1");
foreach (var elem in AllDependences)
Debug.Log("Dependences is " + elem);
}
}
}
// Update is called once per frame
void Update () {
}
}
大家自行运行结果记忆会更深刻。
分析结果发现, ab1.GetAllAssetNames(),ab1.LoadAllAssets() 能取出来的都只有obj1,而我实例化的obj1立方体可是带有材质和贴图的。放开注释的代码
//Object M1 = ab1.LoadAsset("M1");
//Debug.Log(M1.name);
运行会报错,说是空引用。明明有贴图,材质,可我们就是没法从AssetBundle取出来,只可能是unity自己将我们没有显式设置AssetBundle的资源密封起来了,不让我们去获取。
之后的代码
string[] names = ab1Manifest.GetAllAssetNames();
在实验中得到的只有一个文件,就是assetbundlemanifest,不区分大小写。这个文件包含了所有的依赖关系。
再看obj1的依赖项
string[] AllDependences = manifest.GetAllDependencies("obj1");
结果是空的,没有任何依赖项。可是obj1明明依赖了材质M1,还有贴图啊。
猜测,是因为M1等资源没有打包的原因,我们验证一下。
2.将材质,贴图与prefab共同打包到一个AssetBundle下
将M1,T1,T2,T3的AssetBundle设置成obj1,再次打包,最后生成的文件个数没有变化。然而我们运行代码会发现结果出现了部分变化,首先就是M1能够load出来了,验证了只有显示设置AssetBundle name的资源才可以被我们load使用。
最后的依赖资源,manifest.GetAllDependencies("obj1")获取的还是空,看看GetAllDependencies函数定义,获取的是依赖的AssetBundle的名字。obj1依赖的所有东西都在自身所在的AssetBundle里,自然获取不到值。再一次反省,看SDK的注释含义。。。
3.将材质,贴图,prefab分开打包
将M1的AssetBundle设置为M1,重新打包。生成的文件中多了M1的AssetBundle文件。
继续使用加载代码,前面大部分相同,当然这次你不能从obj1的AssetBunble中去load试图获取 M1了。最关键的部分在最后,这次我们可以使用GetAllDependencies获取到obj1的依赖文件M1了。
结语
只打包prefab,省事,但可能造成资源的重复打包,无法复用。如果是唯一性的东西,只有某个物体使用,可以偷懒这么做。但如果想复用东西,老老实实多拆点小的包出来。当然,灵活性最高的就是用4.0那种自己控制的方式。具体的做法根据需求来取舍吧。