[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);
    }


相关文章
|
5月前
|
编解码 算法 图形学
【unity小技巧】减少Unity中的构建打包大小
【unity小技巧】减少Unity中的构建打包大小
161 0
|
6月前
|
缓存 API 图形学
【Unity 3D】AssetBundle打包、上传、加载、卸载详解及演示(附源码)
【Unity 3D】AssetBundle打包、上传、加载、卸载详解及演示(附源码)
267 0
|
6月前
|
算法 图形学 UED
【Unity 3D】AssetBundle工作流程、打包策略详解(超详细必看)
【Unity 3D】AssetBundle工作流程、打包策略详解(超详细必看)
394 0
|
Web App开发 开发者
Chrome 技术篇-将程序打包为crx文件,chrome插件的打包方法。
Chrome 技术篇-将程序打包为crx文件,chrome插件的打包方法。
681 0
Chrome 技术篇-将程序打包为crx文件,chrome插件的打包方法。
|
图形学 索引
|
iOS开发
获取别其他iOS项目中的图片资源(必看)
获取别其他iOS项目中的图片资源(必看)
279 0
获取别其他iOS项目中的图片资源(必看)
|
存储 缓存 API
5.0版本之后的AssetBundle资源的打包和解析加载(Unity3D)
这几天在研究AssetBundle资源打包盒解析加载,也踩过很多坑,参考过很多人的文章 发现很多人关于AssetBundle的文章不是API过时了不能用,就是有点乱 也不是有点乱,就是摸不着头脑,让人不能快速的get到这个东西如何使用 所以我特意在踩过坑之后把我这个学到的经验分享给大家。
|
Java Shell Android开发
Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤(1)
Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤(1)
584 0
Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤(1)
|
XML Java Android开发
Unity是怎么打包APK文件的
大家看过一些第三方组件的接入文档都知道,在Unity里面有几个特殊的文件夹是跟打包APK有关的。首先我们就来了解一下,这些文件夹里面的内容是经历了哪些操作才被放到APK里面的呢? 在Unity的Assets目录下,Plugins/Android无疑是其中的重中之重,首先我们先来看一个常见的Plugins/Android目录是什么样子的。
5232 0
|
图形学 UED
Unity打包/读取AssetBundle资源全教程
Unity 资源AssetBundle打包 本文提供全流程,中文翻译。 Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) Chi...
2211 0