基本介绍:
什么是? 一个assets的集合,打包以用来在运行时加载
在应用中动态的 加载和卸载 新内容。 比如说DLC(downloadable content)
用处?
降低磁盘空间的消耗(第一次发布时) + 热更新资源
AssetBundle 使用LZMA进行压缩,并且相似的Prefabs在打包时能够大大减少空间占用。
创建AssetBundle,使用Editor代码,可以参考官方的manual:
1 public class ExportAssetBundles 2 { 3 [MenuItem("Custom/Build AssetBundle From Selection - Track dependencies")] 4 public static void ExportResource() 5 { 6 string path = EditorUtility.SaveFilePanel("Save Resource","","New Resource","unity3d"); 7 if(path.Length != 0) 8 { 9 Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets); 10 BuildPipeline.BuildAssetBundle(Selection.activeObject,selection,path, 11 BuildAssetBundleOptions.CollectDependencies|BuildAssetBundleOptions.CompleteAssets); 12 Selection.objects = selection; 13 } 14 } 15 16 [MenuItem("Custom/Build AssetBundle From Selection - No dependency tracking")] 17 public static void ExportResourceNoTrack() 18 { 19 string path = EditorUtility.SaveFilePanel("Save Resource", "", "New Resource", "unity3d"); 20 if(path.Length != 0) 21 { 22 23 //BuildPipeline.BuildAssetBundle(Selection.activeObject, Selection.objects, path); 24 BuildPipeline.BuildAssetBundle(Selection.activeObject, Selection.objects, path); 25 } 26 27 } 28 29 }
下载:
1 public class LoadAssetBundle : MonoBehaviour 2 { 3 public Object obj; 4 5 /// <summary> 6 /// 7 /// </summary> 8 /// <typeparam name="T"></typeparam> 9 /// <param name="asset">bundle的名字</param> 10 /// <param name="url"></param> 11 /// <param name="version"></param> 12 /// <returns></returns> 13 public IEnumerator DownloadAssetBundle<T>(string asset,string url,int version) where T: Object 14 { 15 obj = null; 16 #if UNITY_EDITOR 17 18 //prefab = Resources.LoadAssetAtPath("Assets/Artwork/mymodel.fbx", typeof(GameObject)); 19 obj = Resources.LoadAssetAtPath("Assets/" + asset, typeof(T)); 20 yield return null; 21 #else 22 while (!Caching.ready) 23 yield return null; 24 using(WWW www = WWW.LoadFromCacheOrDownload(url,version)) 25 { 26 if (www.error != null) 27 { 28 Debug.LogError("WWW download has an error "+www.error ); 29 } 30 else 31 { 32 AssetBundle bundle = www.assetBundle; 33 obj = bundle.Load(asset, typeof(T)); 34 bundle.Unload(false); 35 } 36 } 37 #endif 38 } 39 40
最好使用上述方式,但使用new WWW(url) 也是可以的。
上述代码中:Resources.loadAsssetAtPath 避免了重新打包assetbundel,这个函数的用处是 使你想load一个assetbundle一样laod它,它会跳过打包的过程来保证asset始终是最新的(简化操作)
.mainAsset 当制作assetbundle的时候的传递的第一个参数。
用处: 存储一个 textAsset 用来表示所有这个assetbundle中的objects信息。
使用注意:
url 使用本地 之前加 file://
从AssetBundle中加载出来的prefab上的脚本是会执行的,只要本地有这个脚本,不然会出现警告:“The referenced script on this Behaviour is missing!” ,脚本不会执行。
如果想要动态的加载脚本,只能通过编译成dll,然后改为.bytes,再反射加载,再add component。这也是官方推荐的Android下热更新脚本的一种方式。
LodaAll 的问题:
首先,对于所有的load,必须要指定type,不然可能由于重名而出现莫名其妙的错误。具体原因可以参见这篇文章的相关介绍:http://www.cnblogs.com/ybgame/p/3973177.html?utm_source=tuicool
常见错误:
Object[] objects = bundle.LoadAll();///
Foreach(Object obj in Objects)
{
Instantiate(obj);
}
注意: 每个prefab 会出来 4个clone;
如果是用 Object[] objects = bundle.LoadAll(Typeof(GameObject));
则: 出来2个clone;
原因:prefab 中包含了 model 的meshfilter。即prefab是包含model的。
解决方法:不是将prefab打包,而是仅仅将model打包。这样就只有一个。
但是如果需要的不仅仅是模型,还要通过加入其他组件,那么就只能采用 名字 来做了
bundle.Load(“name”,typepf(Gameobject));
再instantiate 则是一个。
//使用名字能够保证始终是一个。
考虑第一个AssetBundle的第一个 也就是 mainAsset 保存名字。然后再一一实例化。
另一种:
也可以将这一步放在加载的时候,先加载TextAsset,然后获得名字。
AssetBundle bundle = www.assetBundle;
//textAsset 名称为Names
TextAsset file = bundle.Load("Names", typeof(TextAsset)) as TextAsset;
string content = file.text;
string[] names = content.Split(',');
foreach( string name in names)
{
Instantiate(bundle.Load(name, typeof(GameObject)));
}
关于打包的思路:
尽量把具有相同模型不同脚本的物体做成不同的prefab,然后打包在一起,可以较少空间占用。最后可以直接加载使用。
将大部分 在scene中出现的 都 存成 prefab。
然后 有些 prefab 只是脚本或者位置 或者其他不一样 ,那么这些放在一起打包 ,大大减小包的大小。
完全不关联的东西放 分开 打包。
基础UI可以打成一个包,重复使用
另外基础的场景背景 或者 公用背景 可以打在一起 ,每个场景不同的可以分开。
场景加载中使用Assetbundle的一些思路:
每次加载对应的场景的时候,需要场景对应的
AssetBundle url,每个 AssetBundle 中要加载哪些东西,这会对应着一个文件。然后去构建场景。
类似于MOMO的将场景保存为XML或者Json 文件。http://www.xuanyusong.com/archives/1919
大部分的场景都是基于同一个场景衍生的,所以可以做一个base场景,然后修改后save as
有效率的打包AssetBundle。
随着项目的增大,手动打包很繁杂,所以考虑程序去将项目中的所有需要打包的全部打包。
比如说 可以有一个 txt文件来map Asset –>Assetbundle。
由于WWW.LoadfromCacheOrDownload
是一个异步的过程,
解决方案:
1: 先优先加载 主角 或者 需要优先显示的部分,
再加载其他部分
2: 使用同步加载,AssetBundle.CreatFromFile
要求打包的时候不压缩,所以自己使用LZMA压缩,解压。
AssetBundle下载下来 并且 保存在 本地路径(比如说application.persistantDataPath),再使用AssetBundle.CreatFromFile 。
一个场景的完整的加载过程可能如下:
场景构成:
一个start脚本;
1:读取配置文件
2:加载公有的assetbundle
3:加载私有的assetbundle
具体还要看不同的情况。需要调节加载的顺序,因为会有相互的依赖性,不然可能会出现错误。
配置文件的示例:
Scene1.bytes:
公用bundle 路径(可以是相对的),公用bundle 路径(可以是相对的)| 私有bundle路径,私有bundle路径
分割符 ‘|’ ‘,’
比如: 公用
Application.persistantDatapath + “/publicBundles”+”/road.assetbunlde”;
采用自己定义的格式就可以。
另外,有一个文件记录所有的assetbundle的当前版本,用来供读取。