版本:Unity 6 (6000.0)
语言:英语
构建的输出
AssetBundle 压缩和缓存

原生使用 AssetBundles

可以使用两种不同的 API 来加载 AssetBundles。

AssetBundle.LoadFromFile

AssetBundle.LoadFromFile

当从本地存储加载未压缩和块压缩捆绑包(LZ4)时,此 API 非常高效,因为它可以从磁盘直接增量读取文件内容。使用此方法加载使用完整文件压缩一种存储数据的方 法,可减少其所需的存储空间。请参阅 纹理压缩动画压缩音频压缩构建压缩
请参阅 术语表
(LZMA)压缩的文件效率较低,因为它需要先将文件完全解压缩到内存中。

使用LoadFromFile 的一个示例

using System.IO;
using UnityEngine;

public class LoadFromFileExample : MonoBehaviour
{
    void 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);
    }
}

除了在加载过程中阻塞您的应用程序之外,您还可以使用异步版本AssetBundle.LoadFromFileAsync

如果 AssetBundle 实际上未存储在本地文件中,则可以使用几种其他方法加载 AssetBundle。例如,您可以使用 AssetBundle.LoadFromMemoryAsync

UnityWebRequestAssetBundle

可以调用 UnityWebRequestAssetBundle API 来创建用于下载、缓存和加载 AssetBundles 的特殊网络请求。

通常,URL 将是指向文件的“https://”地址,如网络服务所公开。它也可以是“file://”以访问不支持直接文件系统访问的平台上的本地数据。

此请求可以传递到 DownloadHandlerAssetBundle.GetContent(UnityWebRequestAssetBundle) 以获取加载的 AssetBundle 的 AssetBundle 对象。

例如

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class DownloadExample : MonoBehaviour
{
    IEnumerator Start()
    {
        string uri = "https://myserver/myBundles/bundle123";
        uint crc = 1098980; // Expected content CRC

        UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, crc);
        yield return request.SendWebRequest();

        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
        var loadAsset = bundle.LoadAssetAsync<GameObject>("Assets/Players/MainPlayer.prefab");
        yield return loadAsset;

        Instantiate(loadAsset.asset);
    }
}

或从 2023.1 开始使用 Await 支持

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class DownloadExample : MonoBehaviour
{
    async void Start()
    {
        string uri = "https://myserver/myBundles/bundle123";
        uint crc = 1098980; // Expected content CRC

        UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri, crc);
        await request.SendWebRequest();

        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);
        var loadAsset = bundle.LoadAssetAsync<GameObject>("Assets/Players/MainPlayer.prefab");
        await loadAsset;

        Instantiate(loadAsset.asset);
    }
}

为简便起见,该示例显示了一个在代码中硬编码的 CRC 值。但在实践中,您的 AssetBundles 的预期 CRC 值会在下载 AssetBundles 之前单独下载或从文件检索。请参阅 AssetBundle 下载完整性和安全性

注意:为了避免每次调用此代码时都下载整个 AssetBundle 内容,可以为您的 AssetBundle 启用缓存。这可以通过在调用 UnityWebRequestAssetBundle.GetAssetBundle 时传递 AssetBundle 哈希来完成。哈希值可从 AssetBundle Manifest 中获得,将在本节后面介绍。哈希值充当您请求的 AssetBundle 的精确构建的版本标识符。有关缓存和相关压缩注意事项的详细信息,请参阅 AssetBundle 压缩和缓存

从 AssetBundles 加载资源

现在您已成功加载 AssetBundle,现在该加载一些资源了。

通用代码片段

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

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

在决定如何加载资源时,有几个选项。我们有LoadAssetLoadAllAssets 以及它们的异步对应物LoadAssetAsyncLoadAllAssetsAsync

这是从 AssetBundles 同步加载资源的方法

加载单个资源(例如预制件的根 GameObject)

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

加载所有资源

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

这将返回一个包含每个资源的根对象的数组。

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

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

以及

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

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

加载 AssetBundle Manifest

加载 AssetBundle Manifest 可能非常有用。尤其是在处理 AssetBundle 依赖项时。

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

加载清单本身与从 AssetBundle 加载任何其他资源完全相同

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

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

有关 AssetBundle 依赖项 的前面部分讨论了如何为了从 AssetBundle 加载资源,您必须先加载该 AssetBundle 依赖的任何 AssetBundles。清单对象使动态查找和加载依赖项成为可能,因此您不必在代码中明确硬编码所有 AssetBundle 名称及其关系。假设我们要加载名为“assetBundle”的 AssetBundle 的所有依赖项。

AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath);
AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("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));
}

现在您已经加载了 AssetBundles、AssetBundle 依赖项和资源,现在该谈谈管理所有这些已加载的 AssetBundles 了。

管理已加载的 AssetBundles

注意:Addressables 包提供了一个现成的系统来管理加载 AssetBundles、依赖项和资源。Unity 建议使用 Addressables 而不是自己管理 AssetBundles。

另请参阅:Unity Learn 关于 管理已加载的 AssetBundles 的教程

Unity 不会在对象从活动 场景场景包含游戏的环境和菜单。将每个唯一的场景文件视为一个独特的关卡。在每个场景中,您放置环境、障碍物和装饰,本质上是分段设计和构建游戏。 更多信息
请参阅 术语表
中移除时自动卸载对象。资源清理会在特定时间触发,也可以手动触发。

了解何时加载和卸载 AssetBundle 很重要。不正确地卸载 AssetBundle 会导致内存中出现重复对象或丢失对象。

关于 AssetBundle 管理,最重要的是了解何时调用 AssetBundle.Unload(bool)(或 AssetBundle.UnloadAsync(bool)),以及是否应该在函数调用中传递 true 或 false。Unload 是一个非静态函数,它将通过从内存中移除 AssetBundle 标头和其他数据结构来卸载您的 AssetBundle。该参数指示是否还卸载从该 AssetBundle 实例化的所有对象。

AssetBundle.Unload(true) 会卸载所有从 AssetBundle 加载的 GameObjectsUnity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路点等。GameObject 的功能由附加到它的组件定义。 更多信息
请参阅 术语表
(及其依赖项)。这并不包括您已复制到场景中的对象(例如,通过调用Instantiate),因为该复制数据不属于 AssetBundle。如果场景中的对象引用从 AssetBundle 加载的纹理(并且仍然属于它),则此调用会导致纹理消失,Unity 将其视为丢失纹理。

例如,假设材质 M 从 AssetBundle AB 加载,如以下所示,并用于 预制件一种资源类型,允许您存储一个完整的 GameObject,包括组件和属性。预制件充当模板,您可以从中在场景中创建新的对象实例。 更多信息
请参阅 术语表
P。

如果调用 AB.Unload(true),则活动场景中对象引用的任何 M 实例都将被销毁。

如果您改为调用 AB.Unload(false),则 M 的实例将保留在内存中。它们将成为不链接回原始 AssetBundle 的独立对象。

如果稍后再次加载 AB,Unity 不会将 M 的现有副本重新链接到 AssetBundle 中的材质。

如果您创建了预制件 P 的另一个实例,它将不会使用 M 的现有副本。相反,将加载 M 的第二个副本。

通常,使用AssetBundle.Unload(false) 会导致重复对象和其他问题。大多数项目应使用AssetBundle.Unload(true) 来避免这种情况。有两种常见的策略可以安全地调用AssetBundle.Unload(true)

  • 在应用程序生命周期中定义明确的点,在这些点卸载瞬态 AssetBundles,例如在关卡之间或加载屏幕期间。

  • 为各个对象维护引用计数,并且仅在所有组成对象未被使用时才卸载 AssetBundles。这允许应用程序卸载和重新加载单个对象,而不会重复内存。

如果应用程序必须使用AssetBundle.Unload(false),则只能通过两种方式卸载各个对象

  • 消除对不需要的对象的所有引用,例如,确保没有其他已加载的对象包含指向它的字段。完成此操作后,调用 Resources.UnloadUnusedAssets

  • 非附加地加载场景。这将销毁当前场景中的所有对象并自动调用 Resources.UnloadUnusedAssets

构建的输出
AssetBundle 压缩和缓存