Asset Bundle介绍

AssetBundle是与主游戏或应用程序分开存储并在运行时加载(或下载的,对于移动和在线应用程序而言)的内容。
通过允许客户仅下载和安装所需的部件,这有助于最大程度地减少对网络和系统资源的影响。
例如,一家汽车制造商拥有一个VR应用程序,该应用程序允许客户“试驾”车辆,而不希望将所有车辆都包含在应用程序中,
这会导致下载和安装的体积过大。
资产捆绑包允许客户仅下载他想测试驾驶的车辆,并且其平台可以处理的质量级别。

AssetBundles的优点

AssetBundles也可以用于在发布后更新或添加到内容。
这可能包括可下载的内容,限时促销活动或主题内容,例如与假期相关的模型。
AssetBundles还允许自动更新,并且可以在项目之间重复使用内容。
例如,对于已连接的应用程序,您可以将品牌(例如徽标或介绍性视频)存储为在线AssetBundle。
品牌变更时,您只需要更新服务器上的相关AssetBundle。
通过远程AssetBundle加载这些资产的应用程序会自动显示更新的内容,而不需要更新。
可以对下载这些图形的应用进行预编程,以检查在线AssetBundle中的更改并根据需要更新本地存储的包。

变体

AssetBundle可以进一步分为多个变体。变体是与其一起存储的AssetBundle的选项或子类。
如果您有一个名为Vehicles的AssetBundle,则可能有一个针对汽车的变体,另一个针对卡车的变体。
如果将捆绑资产分配给变量,则必须将该捆绑中的所有资产分配给变量。
您不能在bundlename.variant中拥有资产,而在bundlename(没有变体)中拥有另一资产,其中bundlename两者相同。

为了说明使用变体来协助AssetBundle组织的一种用法,假设我们正在为多控制台视频游戏创建教程级别。
下表列出了一些说明中可能使用的图形,以向播放器显示要按下的按钮。
Controller布局是现代控制台所共有的,但是操作按钮上的标签随平台而异。
在此示例中,尽管标签不同,但顶部动作按钮在两个版本的游戏上都执行相同的动作。
在我们的代码中,我们将为定向图形加载通用变体,但在运行时将加载包含特定系统按钮或键盘图形的变体。

资产捆绑包ControlImage的内容

或者,假设我们捆绑了按键图片,以用于在跨平台桌面应用程序上训练用户。
ConsoleA和ConsoleB可以分别成为Windows和macOS,而TopActionButton可能类似于Ctrl和Cmd。

创建一个AssetBundle

当前,构建AssetBundles的唯一方法是通过脚本编写。
以下脚本将此功能添加到Unity Editor。
BuildAssetBundles有三个参数:应在其中创建AssetBundles的目录,
非标准构建模式的BuildAssetBundleOption(可选)和AssetBundle的构建目标。
在Assets文件夹内创建一个名为Editor的文件夹。
在编辑器中,创建一个名为CreateAssetBundles的新C#脚本。
双击CreateAssetBundles在Visual Studio中将其打开,然后删除所有内容。
输入以下内容:

using UnityEditor;
using System.IO;
using UnityEngine;

public class CreateAssetBundles
{
    [MenuItem("Assets/Build AssetBundles")]
    static void BuildAllAssetBundles()
    {
        string assetBundleDirectory = "Assets/StreamingAssets";
        if (!Directory.Exists(Application.streamingAssetsPath))
        {
            Directory.CreateDirectory(assetBundleDirectory);
        }

        BuildPipeline.BuildAssetBundles(assetBundleDirectory, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget);
    }
}

CreateAssetBundles已完成。
保存更改并关闭Visual Studio。
在Unity编辑器中,您可以在Asset下拉列表的底部找到一个新选项:Build AssetBundles。

CreateAssetBundles 参数

在构建AssetBundle之前,必须存在指定的AssetBundle目录。
BuildAssetBundleOptions参数是可选的,如果未指定任何内容,则默认为None。
一些关键选项是:

目标平台选项

由于AssetBundles所需的格式和处理方式因平台而异,因此必须使用以下选项之一指定目标平台:

创建一个简单的AssetBundle

为了测试加载AssetBundle,我们将使用单个GameObject创建一个AssetBundle。
此工作流程假定您已完成前面的CreateAssetBundles脚本。
在项目的文件夹结构中镜像您的AssetBundle组织,可以更轻松地根据需要查找和更新捆绑的资产。

  • 将一个Sprite拖到您的Assets文件夹中。
  • 在一个新场景中,创建一个名为BundledSpriteObject的GameObject。
  • 附加一个Sprite Renderer组件,并从步骤1开始分配您的Sprite。*在Assets中创建一个名为BundledAssets的文件夹。
    在BundledAssets内,创建一个名为testbundle的文件夹。
  • 将BundledSpriteObject拖入testbundle并将其从层次结构中删除。
  • 在“项目”视图中单击BundledSpriteObject以打开其检查器。
    请特别注意检查器的底部。
  • 单击AssetBundle旁边的None将BundledSpriteObject分配给AssetBundle。

加载本地存储的AssetBundle

假设没有发生错误,我们准备从本地存储加载AssetBundle。
为了方便演示,我们正在Start中加载软件包。
在生产中,仅在必要时才加载AssetBundle。
在X和Y中将主摄像机的变换位置设置为0。*创建一个名为Loader的新GameObject。
创建一个名为BundledObjectLoader的新C#脚本,并将其附加到Loader。
双击BundledObjectLoader在Visual Studio中将其打开。使用下面的代码:

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

public class BundleObjectLoader : MonoBehaviour
{
    public string assetName = "BundledSpriteObject";
    public string bundleName = "testbundle";

    private void Start()
    {
        AssetBundle localAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, bundleName));
        if (localAssetBundle == null)
        {
            Debug.LogError("Failed to load AssetBundle!");
            return;
        }

        GameObject asset = localAssetBundle.LoadAsset<GameObject>(assetName);
        Instantiate(asset);
        localAssetBundle.Unload(false);
    }
}

运行当前场景,会看到之前生成的Bundle文件被实例化到场景中。

退出播放模式。
从Loader中删除BundledObjectLoader组件。

用于加载AssetBundle的命令AssetBundle.LoadFromFile是同步的。
这意味着直到命令完成其任务(AssetBundle已完全加载)后,它才会返回。
对于像我们的示例这样的小捆绑包来说,这很好,但对于更大的东西(如我们在汽车或建筑预可视化程序或AAA视频游戏中可能看到的),
这可能会导致帧速率或响应速度下降到不可接受的程度。
更好的选择是LoadFromFileAsync,它可以作为协程运行,以使项目在加载AssetBundle时继续运行。

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

public class BundleLoaderAsync : MonoBehaviour
{
    public string bundleName = "testbundle";
    public string assetName = "BundledSpriteObject";

    IEnumerator Start()
    {
        AssetBundleCreateRequest asyncBundleRequest = AssetBundle.LoadFromFileAsync(Path.Combine(Application.streamingAssetsPath, bundleName));
        yield return asyncBundleRequest;
        AssetBundle localAssetBundle = asyncBundleRequest.assetBundle;
        if (localAssetBundle == null)
        {
            Debug.LogError("Failed to load AssetBundle!");
            yield break;
        }

        AssetBundleRequest assetRequest = localAssetBundle.LoadAssetAsync<GameObject>(assetName);
        yield return assetRequest;
        GameObject prefab = assetRequest.asset as GameObject;
        Instantiate(prefab);
        localAssetBundle.Unload(false);
    }
}

BundleLoaderAsync已完成。
保存更改并返回到Unity Editor。
按播放。
和以前一样,捆绑对象应加载。
退出播放模式,并从Loader中删除BundleLoaderAsync。

从网络下载AssetBundle与从本地存储加载资产非常相似。
出于我们的目的,该文件托管在本地Web服务器上的根目录中。
创建一个名为BundleWebLoader的C#脚本,并将其附加到Loader。
双击BundleWebLoader在Visual Studio中打开。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BundleWebLoader : MonoBehaviour
{
    public string bundleUrl = "http://127.0.0.1:8887/testbundle";
    public string assetName = "BundledSpriteObject";

    IEnumerator Start()
    {
        using (WWW web = new WWW(bundleUrl))
        {
            yield return web;
            AssetBundle remoteAssetBundle = web.assetBundle;
            if (remoteAssetBundle == null)
            {
                Debug.LogError("Failed to download AssetBundle!");
                yield break;
            }

            Instantiate(remoteAssetBundle.LoadAsset(assetName));
            remoteAssetBundle.Unload(false);
        }
    }
}

有一个简单的Chrome扩展名"200 OK"
它在本地主机上运行Web服务器。

BundleWebLoader完成。
保存更改并返回到Unity Editor。
按播放。
除非出现任何Web连接问题,否则您应该看到与前两个示例相同的结果。
退出播放模式。

结尾

AssetBundles为开发人员,用户和玩家提供了无数种可能性。
您一定会想出新的方法来使它们为您工作,并且掌握它们将使您能够为用户提供更有效,多功能和个性化的体验。

隐藏边栏