Skip to main content

加载AB包

[TOC]

加载AB包有4种方式

  • AssetBundle.LoadFromMemoryAsync
  • AssetBundle.LoadFromFile
  • WWW.LoadfromCacheOrDownload
  • UnityWebRequestDownloadHandlerAssetBundle

AssetBundle.LoadFromMemoryAsync

此函数采用包含 AssetBundle 数据的字节数组。也可以根据需要传递 CRC 值。如果捆绑包采用的是 LZMA 压缩方式,将在加载时解压缩 AssetBundle。LZ4 压缩包则会以压缩状态加载。

以下是如何使用此方法的一个示例:

using UnityEngine;
using System.Collections;
using System.IO;

public class Example : MonoBehaviour
{
IEnumerator LoadFromMemoryAsync(string path)
{
AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return createRequest;
AssetBundle bundle = createRequest.assetBundle;
var prefab = bundle.LoadAsset<GameObject>("MyObject");
Instantiate(prefab);
}
}

但是,这不是实现 LoadFromMemoryAsync 的唯一策略。File.ReadAllBytes(path) 可以替换为获得字节数组的任何所需过程。

AssetBundle.LoadFromFile

从本地存储中加载未压缩的捆绑包时,此 API 非常高效。如果捆绑包未压缩或采用了数据块 (LZ4) 压缩方式,LoadFromFile 将直接从磁盘加载捆绑包。使用此方法加载完全压缩的 (LZMA) 捆绑包将首先解压缩捆绑包,然后再将其加载到内存中。

如何使用 LoadFromFile 的一个示例:

public class LoadFromFileExample : MonoBehaviour {
function Start() {
var myLoadedAssetBundle
= AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle"));

if (myLoadedAssetBundle == null) {
Debug.Log("Failed to load AssetBundle!");
return;
}
var prefab = myLoadedAssetBundle.LoadAsset.<GameObject>("MyObject");
Instantiate(prefab);
}
}

注意:在使用 Unity 5.3 或更早版本的 Android 设备上,尝试从流媒体资源 (Streaming Assets) 路径加载 AssetBundle 时,此 API 将失败。这是因为该路径的内容将驻留在压缩的 .jar 文件中。Unity 5.4 和更高版本则可以将此 API 调用与流媒体资源一起使用。

WWW.LoadFromCacheOrDownload

即将弃用(使用 UnityWebRequest)

此 API 对于从远程服务器下载 AssetBundle 或加载本地 AssetBundle 非常有用。这是一个陈旧且不太理想的 UnityWebRequest API 版本。

从远程位置加载 AssetBundle 将自动缓存 AssetBundle。如果 AssetBundle 被压缩,则将启动工作线程来解压缩捆绑包并将其写入缓存。一旦捆绑包被解压缩并缓存,它就会像 AssetBundle.LoadFromFile 一样加载。

如何使用 LoadFromCacheOrDownload 的一个示例:

using UnityEngine;
using System.Collections;

public class LoadFromCacheOrDownloadExample : MonoBehaviour
{
IEnumerator Start ()
{
while (!Caching.ready)
yield return null;

var www = WWW.LoadFromCacheOrDownload("http://myserver.com/myassetBundle", 5);
yield return www;
if(!string.IsNullOrEmpty(www.error))
{
Debug.Log(www.error);
yield return;
}
var myLoadedAssetBundle = www.assetBundle;

var asset = myLoadedAssetBundle.mainAsset;
}
}

由于在 WWW 对象中缓存 AssetBundle 字节所需的内存开销,建议所有使用 WWW.LoadFromCacheOrDownload 的开发人员都应该确保自己的 AssetBundle 保持较小的大小 - 最多只有几兆字节。此外,还建议在有限内存平台(如移动设备)上运行的开发人员确保其代码一次只下载一个 AssetBundle,以此避免内存峰值。

如果缓存文件夹没有任何空间来缓存其他文件,LoadFromCacheOrDownload 将以迭代方式从缓存中删除最近最少使用的 AssetBundle,直到有足够的空间来存储新的 AssetBundle。如果无法腾出空间(因为硬盘已满,或者缓存中的所有文件当前都处于使用状态),LoadFromCacheOrDownload() 将不会使用缓存,而将文件流式传输到内存中

为了强制执行 LoadFromCacheOrDownload,需要更改版本参数(第二个参数)。仅当传递给函数的版本与当前缓存的 AssetBundle 的版本匹配,才会从缓存加载 AssetBundle。

UnityWebRequest

UnityWebRequest 有一个特定 API 调用来处理 AssetBundle。首先,需要使用 UnityWebRequest.GetAssetBundle 来创建 Web 请求。返回请求后,请将请求对象传递给 DownloadHandlerAssetBundle.GetContent(UnityWebRequest)。GetContent 调用将返回 AssetBundle 对象。

下载捆绑包后,还可以在 DownloadHandlerAssetBundle 类上使用 assetBundle 属性,从而以 AssetBundle.LoadFromFile 的效率加载 AssetBundle。

以下示例说明了如何加载包含两个游戏对象的 AssetBundle 并实例化这些游戏对象。要开始这个过程,我们只需要调用 StartCoroutine(InstantiateObject());

IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request
= UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");
Instantiate(cube);
Instantiate(sprite);
}

使用 UnityWebRequest 的优点在于,它允许开发人员以更灵活的方式处理下载的数据,并可能消除不必要的内存使用。这是比 UnityEngine.WWW 类更新和更优的 API。

从 AssetBundle 加载资源

现在已经成功下载 AssetBundle,因此是时候最终加载一些资源了。

通用代码片段:

T objectFromBundle = bundleObject.LoadAsset<T>(assetName);

T 是尝试加载的资源类型。

决定如何加载资源时有几个选项。我们有 LoadAsset、LoadAllAssets 及其各自的异步对应选项 LoadAssetAsync 和 LoadAllAssetsAsync。

同步从 AssetBundle 加载资源的方法如下:

加载单个游戏对象:

GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);

加载所有资源:

Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();

现在,在前面显示的方法返回要加载的对象类型或对象数组的情况下,异步方法返回 AssetBundleRequest。在访问资源之前,需要等待此操作完成。加载资源:

AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName);
yield return request;
var loadedAsset = request.asset;

以及

AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync();
yield return request;
var loadedAssets = request.allAssets;

加载资源后,就可以开始了!可以像使用 Unity 中的任何对象一样使用加载的对象。

加载 AssetBundle 清单

加载 AssetBundle 清单可能非常有用。特别是在处理 AssetBundle 依赖关系时。

要获得可用的 AssetBundleManifest 对象,需要加载另外的 AssetBundle(与其所在的文件夹名称相同的那个)并从中加载 AssetBundleManifest 类型的对象。

加载清单本身的操作方法与 AssetBundle 中的任何其他资源完全相同:

AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

现在,可以通过上面示例中的清单对象访问 AssetBundleManifest API 调用。从这里,可以使用清单获取所构建的 AssetBundle 的相关信息。此信息包括 AssetBundle 的依赖项数据、哈希数据和变体数据。

别忘了在前面的部分中,我们讨论过 AssetBundle 依赖项以及如果一个捆绑包对另一个捆绑包有依赖性,那么在从原始捆绑包加载任何资源之前,需要加载哪些捆绑包?清单对象可以动态地查找加载依赖项。假设我们想要为名为“assetBundle”的 AssetBundle 加载所有依赖项。

AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 获取依赖的包名
string[] dependencies = manifest.GetAllDependencies("assetBundle"); //传递想要依赖项的捆绑包的名称。
foreach(string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));
}

现在已经加载 AssetBundle、AssetBundle 依赖项和资源,因此是时候讨论如何管理所有这些已加载的 AssetBundle 了。

把下载的AssetBundle 保存到本地

DownloadHandler

https://docs.unity3d.com/cn/2019.4/ScriptReference/Networking.DownloadHandler.html

变量

  • data 返回从远程服务器下载的原始字节,或 null。(只读)
  • isDone 如果此 DownloadHandler 的父 UnityWebRequest 已通知它已接收所有数据,且此 DownloadHandler 已完成所有必要的下载后处理操作,则返回 true。(只读)
  • text 便捷属性。返回解释为 UTF8 字符串的 data 中的字节。(只读)

使用 File.WriteAllBytes(string path, byte[] bytes); 进行保存

参数

  • path 要写入到的文件。

  • bytes 要写入到文件的字节。

    描述

创建新文件,将指定字节数组写入到此文件中,然后关闭该文件。如果目标文件已经存在,它将被覆盖。

string uri = "";
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri);
File.WriteAllBytes("Asset/ABResocuse",request.downloadHandler.data);