您的位置:首页 > 移动开发 > Unity3D

[Unity3d]制作打包并载入AssetBundle

2014-09-25 10:28 309 查看
通常在游戏执行过程中,并不希望一次将全部的资源都载入。而是在资源被使用的时候载入,以免占用过多的存储空间。所以我们可能会尽量规划好不同功能的场景,在需要的时候才载入场景并释放掉前个场景中不需要的资源。或是将资源放在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三种类别的资源,如果存储文件路径名相同则会先删除旧文件。

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

因为载入的资源必须要等待载入完成以确保游戏运作正常,在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卸载(bundle.assetBundle.Unload(false));另外,要特别注意的是载入AssetBundle到卸载期间可能消耗些微时间,如果连续调用载入同一个AssetBundle,造成在卸载前重复载入也可能出现前面的错误,所以,最好是能定义些变量,记录载入的AssetBundle有哪些,避免重复载入。

打包的脚本:

[csharp] view
plaincopyprint?





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";

//安卓建立 AssetBundle

//BuildPipeline.BuildAssetBundle(obj, null,targetPath, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android);

//苹果建立 AssetBundle

//BuildPipeline.BuildAssetBundle(obj, null, targetPath, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets, BuildTarget.iPhone);

//建立 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 ();

}

}

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

下载类WWW

WWW bundle = new WWW(path);

这样的做法是通过一个路径进行下载(无论是服务器路径还是本地路径下载操作都一样)但是bundle只能保存在内存中,也就是退出游戏在进入还得重新下,很显然在游戏中我们不能使用这种方式。

IEnumerator loadBundleOBject(string url)

{

WWW date = WWW.LoadFromCacheOrDownload(url,5);

yield return date;

Instantiate(date.assetBundle.mainAsset);//实例化加载的资源,我加载的是个模型。

date.assetBundle.Unload(false);

}

使用的方法是WWW.LoadFromCacheOrDownload(path,5);参数1:服务器或者本地下载地址 参数2:版本号Unity会下载Assetbundle本地中,它的工作原理是先通过(版本号和下载地址)先在本地去找看有没有这个Assetbundle,如果有直接返回对象,如果没有的话,在根据这个下载地址重新从服务器或者本地下载。这里版本号起到了很重要的作用,举个例子,同一下载地址版本号为1的时候已经下载到本地,此时将版本号的参数改成2 那么它又会重新下载,如果还保持版本号为1那么它会从本地读取,因为本地已经有版本号为1的这个Assetbundle了。你不用担心你的资源本地下载过多,也不用自己手动删除他们,这一切的一切Unity会帮我们自动完成,它会自动删除掉下载后最不常用的Assetbundle
,如果下次需要使用的话只要提供下载地址和版本后它会重新下载。(转自雨松MOMO研究院)

Assetbundle 中的脚本,在移动平台下Assetbundle里面放的脚本是不会被执行的,在手机上将Assetbundle下载到本地后,加载进游戏中Prefab会自动在本地找它身上挂着的脚本,他是根据脚本的名来寻找,如果本地有这条脚本的话,Prefab会把这个脚本重新绑定在自身,并且会把打包前的参数传递进来。如果本地没有,身上挂的条脚本永远都不会被执行。(转自雨松MOMO研究院)

最后再说说我遇到的问题,本地加载.unity3d的资源没问题,但是我挂到IIS上就失败了,显示404错误,找不到资源,无法实例化。这就需要配置服务器的MIME

加载脚本:

[csharp] view
plaincopyprint?





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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: