博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Unity3D技术文档翻译】第1.5篇 本地使用 AssetBundles
阅读量:6081 次
发布时间:2019-06-20

本文共 7433 字,大约阅读时间需要 24 分钟。

Unity3D技术文档翻译

本章原文所在章节:【Unity Manual】→【Working in Unity】→【Advanced Development】→【AssetBundles】→【Using AssetBundles Natively】

本地使用 AssetBundles

从 Unity5 开始,我们可以使用4个不同的 API 来加载 AssetBundles。使用哪个 API,取决于 AssetBundle 在哪个平台上被加载,以及创建 AssetBundles 时使用的是哪种压缩方法(不压缩、LZMA算法、LZ4算法)。

这4个 API 分别是:

  • ’s (Unity 5.3 或者更高版本)

AssetBundle.LoadFromMemoryAsync

这个方法的参数是一个包含了 AssetBundle 数据的字节数组。如果需要的话,你还可以传入一个 CRC(循环冗余校验码) 参数。如果 AssetBundle 使用 LZMA 算法压缩,那么 AssetBundle 在加载的时候会被解压。如果 AssetBundle 使用 LZ4 算法压缩,它将直接以压缩形式被加载。

下面是一个如何使用这个方法的例子:

IEnumerator LoadFromMemoryAsync(string path){    AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));    yield return createRequest;    AssetBundle bundle = createRequest.assetBundle;    var prefab = bundle.LoadAsset.
("MyObject"); Instantiate(prefab);}

当然,这不是唯一使用该方法的方式。参数 File.ReadAllBytes(path) 可以被任何一个字节数组或者一个返回字节数组的方法替换。

AssetBundle.LoadFromFile

这个 API 在加载本地存储的未压缩 AssetBundle 时具有很高效率。如果 AssetBundle 是未压缩,或者是数据块形式(LZ4 算法压缩)的,LoadFromFile 将从磁盘中直接加载它。如果 AssetBundle 是高度压缩(LZMA 算法压缩)的,在将它加载进入内存前,会首先将它解压。

下面是一个如何使用这个方法的例子:

public class LoadFromFileExample extends 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.
("MyObject"); Instantiate(prefab); }}

注意:在安卓设备上,如果 Unity 是5.3或者更老的版本,这个方法在读取资产流路径(Streaming Assets path)的时候会失败。这是因为那个路径是在一个 .jar 文件的内部。Unity5.4 以及更高的版本没有这个问题,可以正常的读取资产流。

WWW.LoadFromCacheOrDownload

这个 API 已经被废弃(建议使用 UnityWebRequest)(三思:这句话不是我加的,官方文档中就是有这句话)

这个 API 对于从远程服务器加载 AssetBundles,或者加载本地 AssetBundles 都很有用。这个 API 是 UnityWebRequest 不尽如人意的老版本。

从远程服务器加载的 AssetBundle 将会被自动缓存。如果 AssetBundle 是压缩形式的,一个工作线程将加速解压这个 AssetBundle 并写入缓存。一旦一个 AssetBundle 已经被解压且被缓存,它将完全像使用 AssetBundle.LoadFromFile 方法一样被加载。

下面是一个如何使用这个方法的例子:

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

由于缓存 AssetBundle 字节数据的开销较大,建议所有开发者在使用 WWW.LoadFromCacheOrDownload 方法时,确保 AssetBundles 都比较小——最多几兆字节。同样建议所有开发者在内存比较有限的平台(比如移动设备)上使用这个方法时,确保同时只下载一个 AssetBundle,防止内存泄漏。

如果缓存文件夹没有足够的空间来缓存额外的文件,LoadFromCacheOrDownload 将会从缓存中迭代删除最近最少使用的 AssetBundles,直到有足够的空间来存储新的 AssetBundle。如果空间还是不够(比如硬盘满了,或者所有缓存的文件都正在被使用),LoadFromCacheOrDownload() 将绕开缓存,直接将文件以流的形式存进内存。

如果想要使用 LoadFromCacheOrDownload 的版本变量,方法参数(第二个参数)需要改变。如果参数与当前缓存的 AssetBundle 的版本变量一致,那么就可以从缓存中加载这个 AssetBundle。

UnityWebRequest

UnityWebRequest 有个专门的 API 来处理 AssetBundles。首先,你需要使用 UnityWebRequest.GetAssetBundle 方法来创建你的 web 请求。在请求返回后,将请求放入 DownloadHandlerAssetBundle.GetContent(UnityWebRequest) 作为参数。GetContent 方法将返回你的 AssetBundle 对象。

在下载完 AssetBundle 后,你同样可以使用 类的 assetBundle 属性来加载 AssetBundle,这就和使用 AssetBundle.LoadFromFile 方法一样高效。

下面有个例子展示:如何加载一个包含两个 GameObjects 的 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
("Cube"); GameObject sprite = bundle.LoadAsset
("Sprite"); Instantiate(cube); Instantiate(sprite);}

使用 UnityWebRequest 的优点是,它允许开发者用更灵活的方式来处理下载的数据,并且潜在地排除了不必要的内存占用。和 UnityEngine.WWW 类相比,这是更现代,也更推荐的 API。

从 AssetBundles 中加载资源

现在,你已经成功下载了你的 AssetBundle,是时候从中加载一些资源。

通常的代码片段:

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

T 是你想加载的资源类型。

当你决定如何加载资源的时候,有一对方法供使用。我们可以使用 LoadAssetLoadAllAssets 方法,以及与它们对应的异步方法: LoadAssetAsyncLoadAllAssetsAsync

下面是一个从一个 AssetBundle 中同步加载资源的例子:

  • 加载一个 GameObject:

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

  • 加载所有资源:

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

现在,和上面展示的方法(要么返回你正在加载的对象,要么返回一组对象)不同的是,异步方法返回的是一个 。

在可以使用资源前,你需要等待处理完成。如下:

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

以及:

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

一旦你已经加载好你的资源,是时候行动了!你可以像使用 Unity 中的其他对象一样使用加载的对象。

加载 AssetBundle Manifests(资源清单)

加载 AssetBundle manifests 非常的有用。尤其是当处理 AssetBundle 依赖关系的时候。

为了获取可以使用的 AssetBundleManifest,你需要加载一个额外的 AssetBundle(即那个和文件夹名称相同的文件),并且从中加载出一个 AssetBundleManifest 类型的对象。

从 AssetBundle 中加载 manifest 完全和从中加载其他资源一样,如下:

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

现在,你可以通过上面例子获取到的 manifest 对象来使用 AssetBundleManifest 类的 API。从现在开始,你可以使用这个 manifest 来获取关于 AssetBundle 的信息,包括:依赖数据、hash 数据,以及版本变量数据。

还记得前面章节我们讨论过的,如果一个 bundleA 对 bundleB 有依赖,那么在从 bundleA 中加载任何资源之前,我们需要先加载 bundleB 吗?Manifest 对象就使得动态查找正在加载的依赖关系成为可能。比如我们想要加载一个名叫“assetBundle”的 AssetBundle 的所有依赖:

AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);AssetBundleManifest manifest = assetBundle.LoadAsset
("AssetBundleManifest");string[] dependencies = manifest.GetAllDependencies("assetBundle"); //Pass the name of the bundle you want the dependencies for.foreach(string dependency in dependencies){ AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency));}

现在,你已经加载了 AssetBundle、AssetBundle 依赖,以及其他资源,是时候讨论如何管理这些加载好的 AssetBundles 了。

(这里可以休息一下,下面是另一块内容)

管理加载好的 AssetBundles

你也可以查看 Unity 的 教程。(篇幅较大,建议先看本节内容。后面我会找时间把这个教程作为补充文档一起翻译了)

在 Objects 被从场景中移除的时候,Unity 不会自动将它们卸载。资源的清理是在某个特定时机被触发,当然也可以手动触发。

知道什么时候加载和卸载一个 AssetBundle 很重要。不合时宜的卸载 AssetBundle 可能导致重复对象(duplicating objects)错误,或者其他未预料到的情况,比如纹理丢失。

理解如何管理 AssetBundle 最重要的事是什么时候调用 方法,以及该方法的参数应该传入 true 还是 false。该方法卸载 AssetBundle 的头信息;方法参数决定了是否同时卸载从 AssetBundle 中加载并实例化的所有 Objects。

如果你传入 true 参数,那么你从 AssetBundle 中加载的所有对象将被卸载,即便这些对象正在被使用。这就是我们前面提到的,导致纹理丢失的原因。

假设 Material M 是从 AssetBundle AB 中加载的,如下:

  • 如果 AB.Unload(true) 被调用,那么任何使用 Material M 的实例都将被卸载并消除,即便它们正在场景中被使用。

  • 如果 AB.Unload(false) 被调用,那么将切断所有使用 Material M 的实例与 AssetBundle AB 的联系。

使用 AB.Unload(false) 后

如果 AssetBundle AB 在被卸载后不久再次被加载,Unity 并不会将已经存在的使用 Material M 的实例与 AssetBundle AB 重新联系。因此将存在两份被加载的 Material M。

AssetBundle 再次被加载后

再次加载预设后

通常情况下,使用 AssetBundle.Unload(false) 不会获得理想情况。大多数项目应该使用 AssetBundle.Unload(true) 方法,以避免内存中出现重复对象(duplicating objects)。

大多数项目应该使用 AssetBundle.Unload(true) 方法,并且要采取措施确保没有重复对象。两种通常采取的措施如下:

  • 在应用的生命周期中找到合适的时机来卸载 AssetBundle,比如关卡之间,或者加载场景的时候。

  • 为每个对象采取引用计数管理方法,只有当 AssetBundle 的所有对象都没有被使用的时候,再卸载 AssetBundle。这样就可以避免应用出现重复对象的问题。

如果应用必须使用 AssetBundle.Unload(false) 方法,对象将只能在以下两种情况下被卸载:

  • 消除对象的所有引用,包括场景中的和代码中的。之后,调用

  • 没有额外附加特性地加载一个场景。这将消除当前场景的所有对象,并自动调用 。

如果你不想自己管理加载的 AssetBundle、依赖关系,以及资源,你可能需要使用 AssetBundle 管理器(AssetBundle Manager,下一章节将介绍)。

转载于:https://www.cnblogs.com/hearthstone/p/7807803.html

你可能感兴趣的文章
韩国最大比特币交易所Bithumb被黑客攻击,损失超过350亿韩元
查看>>
如何在 Scala 中利用 ADT 良好地组织业务
查看>>
几种常见的CSS布局
查看>>
Netflix最新视频优化实践:用更少的带宽打造完美画质
查看>>
基于Spring Boot实现图片上传/加水印一把梭操作
查看>>
关于js、jq零碎知识点
查看>>
有赞跨平台长连接组件设计及可插拔改造
查看>>
高德,腾讯地图 --> 逆地址解析(坐标位置描述)
查看>>
nodejs流之行读取器例子
查看>>
源码|HDFS之NameNode:启动过程
查看>>
[译] 什么是Javascript中的提升
查看>>
阿里巴巴、百度、腾讯都在用的Java架构师知识体系
查看>>
Python 异步网络爬虫 I
查看>>
像 QQ 一样处理滑动冲突
查看>>
01、Handler的那些事
查看>>
Mac OS X x64 环境下覆盖objective-c类结构并通过objc_msgSend获得RIP执行shellcode
查看>>
[译] 如何写出更好的 React 代码?
查看>>
Android动画:这里有一份很详细的 属性动画 使用攻略
查看>>
RxJava2 实战知识梳理(5) 简单及进阶的轮询操作
查看>>
js call,apply,bind总结
查看>>