转载请注明出处!
一、如何使用资源包(AssetBundle)?
使用AssetBundle主要有两大步:第一步是从服务器上或磁盘路径下载AssetBundle,这是用WWW类完成操作;第二步是从资源包(AssetBundle)中加载程序要使用的资源(Assets)。下面是官方的C#脚本案例,创建一个GameObject:
using UnityEngine;
using System.Collections;
public class BundleLoader : MonoBehaviour
{
public string url; //路径
public int version; //版本号
public IEnumerator LoadBundle()
{
while (!Caching.ready)
yield return null;
using (WWW www = WWW.LoadFromCacheOrDownload(url, version))
{
yield return www;
if (www.error != null)
throw new Exception("WWW download had an error:" + www.error);
AssetBundle assetBundle = www.assetBundle;
GameObject gameObject = assetBundle.mainAsset as GameObject; //获取主资源对象
if(gameObject != null)
Instantiate(gameObject);
assetBundle.Unload(false);//释放没用的资源
}
}
void Start()
{
StartCoroutine(LoadBundle()); //使用协程来下载和加载资源
}
}
二、在编辑器(Editor)中如何使用资源包(
AssetBundle
)?
因为创建应用程序是一个迭代的过程,那么通常情况会多次修改资源内容,这就导致我们修改资源后需要重新打包,并且测试它们是否可行。虽然可以在编辑器(Editor)中加载资源包(AssetBundle),但这是不提倡的,相反,在编辑器中测试时我们可以使用
辅助函数
AssetDatabase.LoadAssetAtPath来避免必须使用和修改资源。这个函数会让我们读取正在从资源包(AssetBundle)中加载的资源(Assets),但是会跳过资源的创建过程,而且我们的资源总是最新的。
下面是官方的一个辅助函数的脚本,我们可以取决于是否在编辑器中运行来加载资源。
using UnityEngine;
using System.Collections;
using UnityEditor;
public class AssetBundleLoader
{
public Object Obj; //从AssetBundle中检索的对象
//协程
public IEnumerator LoadBundle<T>(string url, int version, string assetName, string assetPath) where T : Object
{
Obj = null;
#if UNITY_EDITOR
//在编辑器中获取对象的方法
Obj = AssetDatabase.LoadAssetAtPath(assetPath, typeof(T));
if (Obj == null)
Debug.LogError("Asset not found at path: " + assetPath);
yield break;
#else
//其他平台则进行正常的下载和加载
WWW download;
if ( Caching.enabled ) {
while (!Caching.ready)
yield return null;
download = WWW.LoadFromCacheOrDownload( url, version );
}
else {
download = new WWW (url);
}
yield return download;
if ( download.error != null ) {
Debug.LogError( download.error );
download.Dispose();
yield break;
}
AssetBundle assetBundle = download.assetBundle;
download.Dispose();
download = null;
if (assetName == "" || assetName == null)
Obj = assetBundle.mainAsset;
else
Obj = assetBundle.Load(assetName, typeof(T));
assetBundle.Unload(false);
#endif
}
}
使用
上面的脚本,
如果运行打包出来的程序,我们可以从下载的资源包(AssetBundle)中获取资源对象;如果在编辑器中运行程序,我们就可以直接从项目工程文件夹中加载资源。代码示例如下:
using UnityEngine;
using System.Collections;
public class ExampleLoadingBundle : MonoBehaviour
{
public string url = "http://www.mygame.com/myBundle.unity3d"; // 下载的URL
public int version = 1; //AssetBundle的版本号
public string assetName = "MyPrefab"; // 要从资源包中加载的资源名
public string assetPath; // 工程项目中资源的所在路径
private Object ObjInstance; //要创建的对象实例
void Start()
{
StartCoroutine(Download());
}
IEnumerator Download()
{
AssetBundleLoader assetBundleLoader = new AssetBundleLoader();
yield return StartCoroutine(assetBundleLoader.LoadBundle<GameObject>(url, version, assetName, assetPath));
if (assetBundleLoader.Obj != null)
ObjInstance = Instantiate(assetBundleLoader.Obj);
}
void OnGUI()
{
//通过GUI来显示对象信息
GUILayout.Label(ObjInstance ? ObjInstance.name + " instantiated" : "");
}
}
三、怎样缓存资源包(AssetBundles)
?
我们可以使用WWW.LoadFromCacheOrDownload来下载并自动保存缓存资源到磁盘中,但是PC/MAC以及IOS/Android应用程序都有4GB大小的限制,因此如果我们需要更多的空间,可以在官网购买一个单独的缓存许可证。(个人觉得无需购买,除非做的游戏非常大
)
如果资源包没有压缩,并存储在StreamingAssets文件夹中,我们就可以使用AssetBundle.LoadFromFile函数来引用磁盘上的资源包。如果存储在StreamingAssets文件夹中保存的资源包被压缩了,我们需要使用WWW.LoadFromCacheOrDownload方法在缓存中复制一份未压缩的资源包。
四、资源包(AssetBundles)跨平台吗
?
AssetBundle在一些平台上是兼容的,下面是官方给的一个参考表:
| Standalone | IOS | Android |
Editor | Y | Y* | Y* |
Standalone | Y | | |
IOS | | Y | |
Android | | | Y |
(*)移动平台的AssetBundle所包含的数据,是用优化的、平台特定的格式存储的,这跟编辑器(Editor)运行的平台是不兼容的。如果发布游戏时,每一个平台需要不同的资源包,请注意这些资源是有保护性的。尤其是不同平台间的着色器(Shader)是不同的。
例如:GI数据是以ARM架构优化的格式存储的,因此它在X86的COU上是不加载的;着色器(Shaders)数据是以OpenGLES2的配置存储的,因此使用DX9/11渲染器的编辑器不能加载它(但是能在OpenGLCore渲染器上运行
)。在开发过程中,推荐我们在
资源包(AssetBundle)和资源包管理器(AssetBundle Manager)
中使用模拟的方式(
Simulation Mode
)来辅助避免编辑器和移动平台间的不兼容。
五、如何确定资源包(AssetBundle)中的资源(Asset)
?
当我们打包资源包后,内部资源是通过不带扩展名的文件名来确定的。例如,一个纹理在项目工程中的目录是“
Assets/Textures/myTexture.jpg
”,需要使用“
myTexture
”的名称从资源包中加载。
六、可以在另一个游戏中重新使用资源包(AssetBundle)吗
?
资源包允许我们在不同的游戏中公用资源,要求是对象引用的资源也必须包含在资源包中。在Unity5以后的版本中,都是可以公用资源包的。
七、现在打包的资源包(AssetBundle)在
Unity
今后的版本中可以使用吗
?
Unity的所有机制基本都是向前兼容的,资源包也一样。资源包打包后包含一个类型树的结构,这就允许资源类型的信息可以被不同版本的Unity解析。在PC平台上类型树是默认包含的,但是在打包时可以通过
BuildAssetBundleOptions.DisableWriteTypeTree选项禁用。类型树不会包含在移动和控制台资源包中,所以每当序列化格式更改时,我们就需要重新打包。
八、如何将资源包(AssetBundle)的资源对象列出来
?
我们可以使用
AssetBundle.LoadAllAssets函数来获取资源包中所有对象的数组,它不能直接返回唯一标识符的列表。我们通常的解决方法是,用一个文本资源(TextAsset)保存资源包(AssetBundle)中
不同资源的名称,然后通过这个文本资源中的内容来加载资源。
九、一个资源包(AssetBundle)如何引用其他资源包的资源
?
假设资源包A包含一个预设Prefab,而Prefab使用了一个材质球Mat和一张纹理Tex;资源包B包含了一个场景Scene,这个场景中有Prefab的实例化对象,B需要访问材质Mat,除非Mat被包含在了一个特定的资源包中,否则Mat将会被同时打包仅A和B。Tex也一样。
扩展:
如果单个AssetBundle过大,只要这个AssetBundle中有一个资源改变了就需要重新下载整个AssetBundle,浪费流量和玩家的等待时间;如果单个AssetBundle过小,极端情况是每个资源一个AssetBundle,虽然实现了更新最小化,但是带来了额外开销:AssetBundle本身也是有大小的,而且查找加载AssetBundle也是需要时间的。大家都往U盘里面拷过东西,拷一个1G的文件比拷1千个1M的文件要快很多。比较合理的做法是根据逻辑来,例如每个角色可以有独立的AssetBundle,公用的一些UI资源可以打到一个AssetBundle里面,每个场景独立的UI资源可以打成独立的AssetBundle。这样做资源预加载的时候也方便,每个场景需要用到几个Bundle就加载几个Bundle,无关的资源不会被加载。
小弟基本把官方的教程全部分享出来了,可能存在好多不足之处,希望大家能帮忙指出,我会尽快改正。小弟也是刚学习打包AssetBundle不久,爬过了许多坑,个人感觉官方的教程也有好多没有提及的坑,估计好多初学者也会钻入坑中翻来覆去。下一篇博文中将把我遇到的坑和解决方法总结一下分享给大家,并
给出完整的项目源码(包括资源的打包、下载资源包、加载资源包、获取资源几依赖资源、使用资源等)。