集成 Asset Delivery (Unity)

集成 Asset Delivery 时,Unity 游戏可以使用 Addressables 或 AssetBundle 访问资源包。Addressables 是最近推出且推荐使用的 Asset Delivery 解决方案,适用于使用 Unity 2019.4 或更高版本构建的游戏;AssetBundle 则支持 Unity 2017.4 和 2018.4 中的资源包。

Unity Addressables

使用 Unity 2019.4 或更高版本构建的游戏应将 Addressables 用于 Android 设备上的 Asset Delivery。Unity 会提供 Play Asset Delivery (PAD) API,以便使用 Addressables 来处理 Android 资源包。如需了解如何使用 Addressables,请参阅以下资源:

使用 AssetBundle 文件

使用 Unity 2017.4 和 2018.4 构建的游戏可以将 AssetBundle 文件用于 Android 设备上的 Asset Delivery。Unity AssetBundle 文件包含可在应用运行时由 Unity 引擎加载的序列化资源。这些文件是平台专用的(例如,针对 Android 构建),并且可与资源包结合使用。最常见的情况是,将一个 AssetBundle 文件打包成单个资源包,并且资源包与 AssetBundle 使用相同的名称。如果您希望更灵活地创建资源包,请使用 API 配置资源包。

在运行时,可使用 Play Asset Delivery for Unity 类来检索打包在资源包中的 AssetBundle。

前提条件

  1. 适用于 Unity 的 Google 软件包中下载最新版本的 Play Asset Delivery Unity 插件。

  2. 在 Unity 中创建 AssetBundle

使用界面配置 AssetBundle

  1. 配置资源包中的每个 AssetBundle:

    1. 依次选择 Google > Android App Bundle > Asset Delivery Settings
    2. 如需选择直接包含 AssetBundle 文件的文件夹,请点击 Add Folder

  2. 针对每个 Bundle,将 Delivery Mode 更改为 Install TimeFast FollowOn Demand。解决所有错误或依赖项,然后关闭窗口。

  3. 依次选择 Google > Build Android App Bundle 以构建 App Bundle。

  4. (可选)配置 App Bundle 以支持不同的纹理压缩格式

使用 API 配置资源包

您可以通过能作为自动化构建系统的一部分运行的编辑器脚本配置 Asset Delivery。

使用 AssetPackConfig 类定义需要包含在 Android App Bundle build 中的资源,以及这些资源的分发模式。这些资源包无需包含 AssetBundle。

public void ConfigureAssetPacks {
   // Creates an AssetPackConfig with a single asset pack, named
   // examplePackName, containing all the files in path/to/exampleFolder.
   var assetPackConfig = new AssetPackConfig();
   assetPackConfig.AddAssetsFolder("examplePackName",
                                   "path/to/exampleFolder",
                                   AssetPackDeliveryMode.OnDemand);

   // Configures the build system to use the newly created assetPackConfig when
   // calling Google > Build and Run or Google > Build Android App Bundle.
   AssetPackConfigSerializer.SaveConfig(assetPackConfig);

   // Alternatively, use BundleTool.BuildBundle to build an App Bundle from script.
   BuildBundle(new buildPlayerOptions(), assetPackConfig);
}

在给定 BuildPlayerOptionsAssetPackConfig 的情况下,您还可以在 Bundletool 类中使用静态 BuildBundle 方法生成具有资源包的 Android App Bundle。

如需指导教程,请参阅“在 Unity 游戏中使用 Play Asset Delivery”Codelab

与 Play Asset Delivery Unity API 集成

Play Asset Delivery Unity API 提供了用于请求资源包、管理下载内容和获取资源的功能。请务必先将 Unity 插件添加到您的项目中。

您在该 API 中使用的函数取决于您创建资源包的方式。

如果您使用插件界面创建资源包,请选择插件配置的资源包

如果您使用 API(或插件界面)创建资源包,请选择 API 配置的资源包

根据您希望获取的资源包的分发类型实现该 API。这些步骤如以下流程图所示。

针对插件的资源包流程图

图 1. 获取资源包流程图

检索 AssetBundle

导入 Play Asset Delivery 库,然后调用 RetrieveAssetBundleAsync() 方法检索 AssetBundle。

using Google.Play.AssetDelivery;

// Loads the AssetBundle from disk, downloading the asset pack containing it if necessary.
PlayAssetBundleRequest bundleRequest = PlayAssetDelivery.RetrieveAssetBundleAsync(asset-bundle-name);

安装时分发

配置为 install-time 的资源包可以在应用启动后立即使用。您可以使用以下代码加载 AssetBundle 中的场景:

AssetBundle assetBundle = bundleRequest.AssetBundle;

// You may choose to load scenes from the AssetBundle. For example:
string[] scenePaths = assetBundle.GetAllScenePaths();
SceneManager.LoadScene(scenePaths[path-index]);

快速跟进式分发和按需分发

以下几部分适用于 fast-followon-demand 资源包。

查看状态

每个资源包都存储于应用的内部存储空间内单独的文件夹中。使用 isDownloaded() 方法确定资源包是否已下载。

监控下载

查询 PlayAssetBundleRequest 对象以监控请求的状态:

// Download progress of request, between 0.0f and 1.0f. The value will always be
// 1.0 for assets delivered as install-time.
// NOTE: A value of 1.0 will only signify the download is complete. It will still need to be loaded.
float progress = bundleRequest.DownloadProgress;

// Returns true if:
//   * it had either completed the download, installing, and loading of the AssetBundle,
//   * OR if it has encountered an error.
bool done = bundleRequest.IsDone;

// Returns status of retrieval request.
AssetDeliveryStatus status = bundleRequest.Status;
switch(status) {
    case AssetDeliveryStatus.Pending:
        // Asset pack download is pending - N/A for install-time assets.
    case AssetDeliveryStatus.Retrieving:
        // Asset pack is being downloaded and transferred to app storage.
        // N/A for install-time assets.
    case AssetDeliveryStatus.Available:
        // Asset pack is downloaded on disk but NOT loaded into memory.
        // For PlayAssetPackRequest(), this indicates that the request is complete.
    case AssetDeliveryStatus.Loading:
        // Asset pack is being loaded.
    case AssetDeliveryStatus.Loaded:
        // Asset pack has finished loading, assets can now be loaded.
        // For PlayAssetBundleRequest(), this indicates that the request is complete.
    case AssetDeliveryStatus.Failed:
        // Asset pack retrieval has failed.
    case AssetDeliveryStatus.WaitingForWifi:
        // Asset pack retrieval paused until either the device connects via Wi-Fi,
        // or the user accepts the PlayAssetDelivery.ShowCellularDataConfirmation dialog.
    default:
        break;
}

下载内容较大

超过 150MB 的资源包可以自动下载,但前提是必须已连接到 WLAN。如果用户未连接到 WLAN,PlayAssetBundleRequest 状态会设置为 AssetDeliveryStatus.WaitingForWifi,下载也会暂停。在这种情况下,要么等到设备连接到 WLAN 再恢复下载,要么提示用户批准通过移动网络连接下载资源包。

if(bundleRequest.Status == AssetDeliveryStatus.WaitingForWifi) {
    var userConfirmationOperation = PlayAssetDelivery.ShowCellularDataConfirmation();
    yield return userConfirmationOperation;

    switch(userConfirmationOperation.GetResult()) {
        case ConfirmationDialogResult.Unknown:
            // userConfirmationOperation finished with an error. Something went
            // wrong when displaying the prompt to the user, and they weren't
            // able to interact with the dialog. In this case, we recommend
            // developers wait for Wi-Fi before attempting to download again.
            // You can get more info by calling GetError() on the operation.
        case ConfirmationDialogResult.Accepted:
            // User accepted the confirmation dialog - download will start
            // automatically (no action needed).
        case ConfirmationDialogResult.Declined:
            // User canceled or declined the dialog. Await Wi-Fi connection, or
            // re-prompt the user.
        default:
            break;
    }
}

取消请求(仅限按需)

如果您在 AssetBundle 加载到内存中之前需要取消请求,请对 PlayAssetBundleRequest 对象调用 AttemptCancel() 方法:

// Will only attempt if the status is Pending, Retrieving, or Available - otherwise
// it will be a no-op.
bundleRequest.AttemptCancel();

// Check to see if the request was successful by checking if the error code is Canceled.
if(bundleRequest.Error == AssetDeliveryErrorCode.Canceled) {
    // Request was successfully canceled.
}

异步请求资源包

在大多数情况下,您应使用协程异步请求资源包并监控进度,如下所示:

private IEnumerator LoadAssetBundleCoroutine(string assetBundleName) {

    PlayAssetBundleRequest bundleRequest =
        PlayAssetDelivery.RetrieveAssetBundleAsync(assetBundleName);

    while (!bundleRequest.IsDone) {
        if(bundleRequest.Status == AssetDeliveryStatus.WaitingForWifi) {
            var userConfirmationOperation = PlayAssetDelivery.ShowCellularDataConfirmation();

            // Wait for confirmation dialog action.
            yield return userConfirmationOperation;

            if((userConfirmationOperation.Error != AssetDeliveryErrorCode.NoError) ||
               (userConfirmationOperation.GetResult() != ConfirmationDialogResult.Accepted)) {
                // The user did not accept the confirmation - handle as needed.
            }

            // Wait for Wi-Fi connection OR confirmation dialog acceptance before moving on.
            yield return new WaitUntil(() => bundleRequest.Status != AssetDeliveryStatus.WaitingForWifi);
        }

        // Use bundleRequest.DownloadProgress to track download progress.
        // Use bundleRequest.Status to track the status of request.

        yield return null;
    }

    if (bundleRequest.Error != AssetDeliveryErrorCode.NoError) {
        // There was an error retrieving the bundle. For error codes NetworkError
        // and InsufficientStorage, you may prompt the user to check their
        // connection settings or check their storage space, respectively, then
        // try again.
        yield return null;
    }

    // Request was successful. Retrieve AssetBundle from request.AssetBundle.
    AssetBundle assetBundle = bundleRequest.AssetBundle;

如需详细了解如何处理错误,请参阅 AssetDeliveryErrorCodes 列表。

其他 Play Core API 方法

以下是您可能希望在应用中使用的一些其他 API 方法。

检查下载内容大小

向 Google Play 发出异步调用并针对操作何时完成设置回调方法,从而检查 AssetBundle 的大小:

public IEnumerator GetDownloadSize() {
   PlayAsyncOperation<long> getSizeOperation =
   PlayAssetDelivery.GetDownloadSize(assetPackName);

   yield return getSizeOperation;
   if(operation.Error != AssetDeliveryErrorCode.NoError) {
       // Error while retrieving download size.
    } else {
        // Download size is given in bytes.
        long downloadSize = operation.GetResult();
    }
}

移除 AssetBundle

您可以移除当前未加载到内存中的快速跟进式分发和按需分发 AssetBundle。发出以下异步调用,并针对何时完成设置回调方法:

PlayAsyncOperation<string> removeOperation = PlayAssetDelivery.RemoveAssetPack(assetBundleName);

removeOperation.Completed += (operation) =>
            {
                if(operation.Error != AssetDeliveryErrorCode.NoError) {
                    // Error while attempting to remove AssetBundles.
                } else {
                    // Files were deleted OR files did not exist to begin with.
                }
            };

后续步骤

在本地和通过 Google Play 测试 Asset Delivery 情况