[Unity3d]制作打包并载入AssetBundle

简介:

通常在游戏执行过程中,并不希望一次将全部的资源都载入。而是在资源被使用的时候载入,以免占用过多的存储空间。所以我们可能会尽量规划好不同功能的场景,在需要的时候才载入场景并释放掉前个场景中不需要的资源。或是将资源放在Resources文件下,在需要使用的时候用Resources.Load()方法把资源载入。这些者是不错的管理方法,但是当我们游戏中的资源相当多时,输出的程序文件还是会很大。而且如果是时常会更新内容资源的游戏,也会因为过大的资源造成编译输出过长的后果,特别是手机或是网页游戏。最后编译出来的只是一个文件,如果文件非常大,将可能造成下载或启动游戏的不方便;这时候我们可能就要考虑制作AssetBundle。

制作游戏时,将资源和主程序部分分离的话,可以得到某种程度的好处,我们可以让游戏主程序只管理游戏的逻辑部分。当需要某些资源则从外部的AssetBundle载入资源,这样一来,如果有任何游戏更新只是添加或更换资源,并没有变更到程序的逻辑部分。只需要重新打包资源就可以完成更新,而不需要重新编译主程序并重新发布。

例如我们有已上架的iPhone游戏,因为特定节日想把游戏内的图片换成另一种气氛。通常必须要重新输出,然后送审。等到节日过后又想把图片换回原本的,要再次编译新的程序,再送审,一方面送审费时费工,另一方面是从送审到架上游戏更新完成的时间,我们并不能准确掌控;如果事先将资源分离打包成AssetBundle存储在我们自己管理的服务器上,在这种需要换图的时刻,只需要将自己服务器上的AssetBundle更新即可,可以省掉不断送审的麻烦,时间上也更能掌控,另外就是游戏主程序中没有大的资源,主程序会小一点,那么玩家要下载我们的游戏也会更快速一些。


还有就是,我们编译网页游戏时一定会发现,编译出来的只是一个html文件和一个unity3d文件。写过网页的人都知道,网页的运作是浏览器将网页中的文件、图片、音乐、视频等等下载到客户端暂存之后再执行呈现出结果,那么如果我们游戏内容的庞大资源与主程序编在一起的话,那么编译出来的unity3d文件也会很大,此时我们可能要思考一下,我们希望玩家花多久时间完成启动页面再进行游戏呢?也许,Unity的Web Player有办法解决这个问题使玩家不会花太多时间启动页面(传说中流模式),但是正因为网页游戏运作方式是如此,所以当我们在服务器更新时,玩家只要没有重新载入页面,玩到的游戏内容始终是旧版的,此时又要考虑到,如果没有更新游戏逻辑或内容规则的情况下,是否有必要强制玩家中断游戏重新载入页面;如果将资源打包成AssetBundle分离出来,那么unity3d文件这个主程序的部分将会变得比较小,而当有任何资源更新时只需要在服务器端将AssetBundle换掉,玩家不需要重新载入页面,一样能体验到更新后的内容。

以上写了一大堆,主要就是要说明AssetBundle是个很重要又很好用的东西,毕竟在没变更程序或游戏部署的情况下, 没必要去重新编译游戏主程序,以下就来说明如何制作AssetBundle:

首先要使用[Unity插件:自定义菜单命令]的方法自定义菜单命令,使我们能够何时去打包。接下来我们需要能够指定哪些资源进行打包,所以需要使用到Editor class的Selection.GetFiltered(),还有就是要过滤哪些类辊的资源才是我们要的,选择错误略过不处理,最后使用BuildPipeline.BuilAssetBundle()建立AssetBundle即可,以下范例会在Unity项目目录下建立_AssetBundle目录用来存放打包好的AssetBundle,打包的目标为GameObject, Texture2D, Material三种类别的资源,如果存储文件路径名相同则会先删除旧文件。

[c#]
[MenuItem("Custom Editor/Create AssetBunldes")]
static void ExecCreateAssetBunldes() {

// AssetBundle 的目录名及扩展名
string targetDir = “_AssetBunldes”;
string extensionName = “.assetBundles”;

//取得在 Project 视图中选择的资源(包含子目录中的资源)
Object[] SelectedAsset = Selection.GetFiltered(typeof (Object), SelectionMode.DeepAssets);

//建立存放 AssetBundle 的目录
if(!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);

foreach(Object obj in SelectedAsset){
// 资源文件的路径
string sourcePath = AssetDatabase.GetAssetPath(obj);

// AssetBundle 存储路径
string targetPath = targetDir + Path.DirectorySeparatorChar + obj.name + extensionName;
if(File.Exists(targetPath)) File.Delete(targetPath);

if(!(obj is GameObject) && !(obj is Texture2D) && !(obj is Material)) continue;

//建立 AssetBundle
if(BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)){
Debug.Log(obj.name + “建立完成”);
} else {
Debug.Log(obj.name + “建立失败”);
}
}
}
[/c#]

以上是直接将选择的资源打包成AssetBundle,另外,你也可以在打包前,依需求创建好GameObject并添加Component,建立prefab,最后再以Prefab打包成assetBundle,一切看个人如何运用了。

制作好AssetBundle最终目的还是要在游戏中载入,以下用简短范例在游戏画面上显示一个按钮,当按下按钮之后载入类别为GameObject的AsseBundle并利用Instantiate()使其实例化而在场景中产生GameObject。

[c#]
void OnGUI(){
if(GUI.Button(new Rect(5,35,100,25) , “Load GameObject”)) {
StartCoroutine(LoadGameObject());
}
}

private IEnumerator LoadGameObject() {

// AssetBundle 文件路径
string path = string.Format(“file://{0}/../_AssetBunldes/{1}.assetBunldes” , Application.dataPath , “TestGameObject”);

// 载入 AssetBundle
WWW bundle = new WWW(path);

//等待载入完成
yield return bundle;

//实例化 GameObject并等待作业完成
yield return Instantiate(bundle.assetBundle.mainAsset);

//卸载 AssetBundle
bundle.assetBundle.Unload(false);
}
[/c#]

上面代码上AssetBundle文件路径path是以在Editor中实行为例,具体平台要按实际平台来写。因为载入的资源必须要等待载入完成以确保游戏运作正常,在C#中使用yield除了返回类型要是IEnumerator之外,调用时也必须使用StartCoroutine()调用,如果使用Javascript则可直接调用函数。如果AssetBundle已载入过,下次再请求载入将会出现[The asset bundle ‘文件名’ can’t be loaded because another asset bundle with the same files are already loaded]的错误信息,所以必须在每次载入使用完后,将AssetBundle卸载;另外,要特别注意的是载入AssetBundle到卸载期间可能消耗些微时间,如果连续调用载入同一个AssetBundle,造成在卸载前重复载入也可能出现前面的错误,所以,最好是能定义些变量,记录载入的AssetBundle有哪些,避免重复载入。



打包的脚本:

using UnityEngine; using System.Collections; using UnityEditor;  public class Test : Editor {      [MenuItem("Custom Editor/Create AssetBunldes Main")] 	static void CreateAssetBunldesMain () 	{         //获取在Project视图中选择的所有游戏对象 		Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);           //遍历所有的游戏对象 		foreach (Object obj in SelectedAsset)  		{ 			//本地测试:建议最后将Assetbundle放在StreamingAssets文件夹下,如果没有就创建一个,因为移动平台下只能读取这个路径 			//StreamingAssets是只读路径,不能写入 			//服务器下载:就不需要放在这里,服务器上客户端用www类进行下载。 			string targetPath = Application.dataPath + "/StreamingAssets/" + obj.name + ".assetbundle"; 			if (BuildPipeline.BuildAssetBundle (obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies)) {   				Debug.Log(obj.name +"资源打包成功"); 			}  			else  			{  				Debug.Log(obj.name +"资源打包失败"); 			} 		} 		//刷新编辑器 		AssetDatabase.Refresh ();	 		 	} 	 	[MenuItem("Custom Editor/Create AssetBunldes ALL")] 	static void CreateAssetBunldesALL () 	{ 		 		Caching.CleanCache ();    		string Path = Application.dataPath + "/StreamingAssets/ALL.assetbundle";   		 		Object[] SelectedAsset = Selection.GetFiltered (typeof(Object), SelectionMode.DeepAssets);   		foreach (Object obj in SelectedAsset)  		{ 			Debug.Log ("Create AssetBunldes name :" + obj); 		} 		 		//这里注意第二个参数就行 		if (BuildPipeline.BuildAssetBundle (null, SelectedAsset, Path, BuildAssetBundleOptions.CollectDependencies)) { 			AssetDatabase.Refresh (); 		} else { 			 		} 	} 	 	[MenuItem("Custom Editor/Create Scene")] 	static void CreateSceneALL () 	{ 		//清空一下缓存 		Caching.CleanCache(); 		string Path = Application.dataPath + "/MyScene.unity3d"; 		string  []levels = {"Assets/Level.unity"};     	//打包场景     	BuildPipeline.BuildPlayer( levels, Path,BuildTarget.WebPlayer, BuildOptions.BuildAdditionalStreamedScenes); 		AssetDatabase.Refresh (); 	} 	 }




加载脚本:

private IEnumerator LoadScene()     {         WWW download = WWW.LoadFromCacheOrDownload("http://192.168.1.200/xiaowei/dxw.assetbundle", 1);         yield return download;         //var bundle = download.assetBundle;         //yield return Instantiate(download.assetBundle.mainAsset);          //GameObject obj = (GameObject)download.assetBundle.Load("dxw");         if (download.assetBundle.Contains("dxw"))         {             print(1);             obj = (GameObject)download.assetBundle.Load("dxw");             print(obj);             obj.transform.Rotate(new Vector3(0,0,360));         }                  //Application.LoadLevel("Level");         //卸载 AssetBundle         //download.assetBundle.Unload(false);     }

























本文转蓬莱仙羽51CTO博客,原文链接:http://blog.51cto.com/dingxiaowei/1366214,如需转载请自行联系原作者
相关文章
|
图形学 Android开发 iOS开发
|
编解码 程序员 atlas
Unity 之 图集属性详解和代码示例 -- 拓展一键自动打包图集工具
图集只是当所有给低昂的纹理需要相同的着色器时采用的一种方法,如果一些纹理需要通过着色器应用独立的图形效果,它们就必须分离到自己的材质中,并在单独的组中打图集。
1384 0
Unity 之 图集属性详解和代码示例 -- 拓展一键自动打包图集工具
|
4月前
|
缓存 API 图形学
【Unity 3D】AssetBundle打包、上传、加载、卸载详解及演示(附源码)
【Unity 3D】AssetBundle打包、上传、加载、卸载详解及演示(附源码)
71 0
|
4月前
|
算法 图形学 UED
【Unity 3D】AssetBundle工作流程、打包策略详解(超详细必看)
【Unity 3D】AssetBundle工作流程、打包策略详解(超详细必看)
56 0
|
7月前
|
图形学 Android开发
Unity打包安卓报http请求错误
Unity打包安卓报http请求错误
68 1
|
Java 图形学
Unity打包符号表 使用ndk addr2line.exe+符号表 将崩溃内存地址解析成函数名
符号表的路径,符号表发布出来的时候是一个zip文件要把它解压出来,里面会有两个文件:arm64-v8a(64位)、armeabi-v7a(32位)不过unity默认打包出来的都是64位的程序,所以这个前面加上你的真实路径+arm64-v8a\libil2cpp.sym.so就可以了。
|
前端开发 API 图形学
Unity 实现批量Build打包
Unity 实现批量Build打包
222 1
Unity 实现批量Build打包
|
API 开发工具 图形学
Pico neo3 Unity打包设置
最近使用Pico的频率很高,想给一些Pico爱好者分享一下在Unity中想项目打包到Pico设备中
775 0
Pico neo3 Unity打包设置
|
存储 监控 Java
Android 打包AAB+PAD(Unity篇)(下)
Play Asset Delivery Unity API 集成 检索 AssetBundles 安装时交货 快速跟进和按需交付 检查状态 监控下载 大量下载 取消请求(仅限按需) 异步请求资产包 其他 Play Core API 方法 检查下载大小 移除 AssetBundles 测试 行为 限制 使用内部应用共享进行测试
611 0
Android 打包AAB+PAD(Unity篇)(下)
|
存储 安全 API
Android 打包AAB+PAD(Unity篇)(上)
AAB Play Asset Delivery 分发模式 资源更新 纹理压缩格式定位 应用版本更新 下载大小上限 针对 Unity 构建 使用 UI 配置 AssetBundles 使用 API 配置资产包
1253 0
Android 打包AAB+PAD(Unity篇)(上)