整合資產提供功能 (Unity)

整合資產提供功能時,Unity 遊戲可以使用 Addressable 或 AssetBundle 存取資產包。Addressable 是我們最近建議使用的資產提供解決方案,適用於使用 Unity 2019.4 以上版本建構的遊戲,而 AssetBundle 則支援 Unity 2017.4 和 2018.4 中的資產包。

Unity Addressable

若是使用 Unity 2019.4 以上版本建構的遊戲,應使用 Addressable 在 Android 上提供資產。Unity 提供 Play Asset Delivery (PAD) API,以便使用 Addressable 處理 Android 資產包。如要瞭解如何使用 Addressable,請參閱下列資源:

使用 AssetBundle 檔案

若是使用 Unity 2017.4 和 2018.4 建構的遊戲,可以使用 AssetBundle 檔案在 Android 上提供資產。Unity 的 AssetBundle 檔案具有序列化的資產,可在應用程式執行時由 Unity 引擎載入。這些檔案依平台而異 (例如專為 Android 建構的檔案),可與資產包搭配使用。大多數情況下,一個 AssetBundle 檔案會封裝成單一資產包,以及名稱與 AssetBundle 相同的套件。如果想更靈活地建立資產包,請使用 API 設定資產包。

請在執行階段,使用 Unity 專屬 Play Asset Delivery 類別擷取封裝於資產包內的 AssetBundle。

必要條件

  1. 設定開發環境:

OpenUPM-CLI

如果您已安裝 OpenUPM CLI,可以使用下列指令安裝 OpenUPM 登錄:

openupm add com.google.play.assetdelivery

OpenUPM

  1. 選取 Unity 選單選項「Edit」>「Project Settings」>「Package Manager」,開啟套件管理工具設定

  2. 將 OpenUPM 新增為 Package Manager 視窗中的有範圍限制的登錄檔:

    Name: package.openupm.com
    URL: https://package.openupm.com
    Scopes: com.google.external-dependency-manager
      com.google.play.common
      com.google.play.core
      com.google.play.assetdelivery
      com.google.android.appbundle
    
  3. 選取 Unity 選單選項「Window」>「Package Manager」,即可開啟「package manager」選單。

  4. 將管理員範圍下拉式選單設為「我的註冊中心」

  5. 從套件清單中選取「Google Play Integrity 外掛程式 (適用於 Unity)」套件,然後按下「Install」

從 GitHub 匯入

  1. 從 GitHub 下載最新的 .unitypackage 版本。

  2. 選取 Unity 選單選項「Assets」>「Import package」>「Custom Package」,然後匯入所有項目,即可匯入 .unitypackage 檔案。

  1. 在 Unity 中建立 AssetBundle

透過 UI 設定 AssetBundle

  1. 設定資產包中的各項 AssetBundle:

    1. 依序選取「Google」>「Android App Bundle」>「Asset Delivery Settings」
    2. 若要直接選取含有 AssetBundle 檔案的資料夾,請按一下「Add Folder」

  2. 將每個套件的「Delivery Mode」變更為「Install Time」、「Fast Follow」或「On Demand」。解決任何錯誤或依附元件,接著關閉視窗。

  3. 依序選取「Google」>「Build Android App Bundle」,建立應用程式套件。

  4. (選用) 調整應用程式套件的設定,支援不同的紋理壓縮格式

使用 API 設定資產包

您可以透過編輯器指令碼設定資產提供功能,該指令碼可做為自動建構系統的一部分執行。

請使用 AssetPackConfig 類別,定義要在 Android App Bundle 版本中加入哪些資產,並定義資產提供模式。這些資產包不需要包含 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);
}

您也可以使用 Bundletool 類別中的靜態 BuildBundle 方法,產生內含資產包的 Android App Bundle,但前提是要有 BuildPlayerOptionsAssetPackConfig

如需輔助教學文件,請參閱在 Unity 遊戲程式碼研究室中使用 Play Asset Delivery 一文。

與 Play Asset Delivery Unity API 整合

Play Asset Delivery Unity API 提供的功能包括要求資產包、管理下載內容及存取資產等。請務必先將 Unity 外掛程式新增至專案。

您使用 API 的功能取決於您建立資產包的方式。

如果您使用外掛程式 UI 建立資產包,請選取「外掛程式設定的資產包」

如果您使用 API (或外掛程式 UI) 建立資產包,請選取「API 設定的資產包」

無論您想存取的資產包交付類型為何,其 API 是類似的。步驟請參見下方流程圖。

API 的資產包流程圖

圖 1. 存取資產包的流程圖

擷取資產包

如要在磁碟中還沒有最新版資產包時下載資產包,請匯入 Play Asset Delivery 程式庫,並呼叫 RetrieveAssetPackAsync() 方法。

using Google.Play.AssetDelivery;

// After download, the assets and/or AssetBundles contained in the asset pack
// are not loaded into memory.
PlayAssetPackRequest request = PlayAssetDelivery.RetrieveAssetPackAsync(assetPackName);

安裝時提供

設定為 install-time 的資產包可立即在應用程式啟動時使用,但您必須將其資產載入記憶體。請參閱將資產載入記憶體

快速追蹤及隨選傳遞

這部分的內容適用於 fast-followon-demand 資產包。

檢查狀態

每個資產包會儲存在應用程式內部儲存空間的獨立資料夾中。請使用 isDone() 方法,判斷資產包是否已下載且可供使用,或是否發生錯誤。

監控下載內容

查詢 PlayAssetPackRequest 物件以監控要求的狀態

// 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 does not mean that the request has completed, only that
// the DOWNLOADING stage is finished.
float progress = request.DownloadProgress;

// Returns the status of the retrieval request.
// If the request completed successfully, this value should be AssetDeliveryStatus.Available.
// If an error occurred, this value should be AssetDeliveryStatus.Failed.

AssetDelivery status = request.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.Failed:
        // Asset pack retrieval failed.
    case AssetDeliveryStatus.WaitingForWifi:
        // Asset pack retrieval paused until either the device connects via Wi-Fi,
        // or the user accepts the PlayAssetDelivery.ShowConfirmationDialog dialog.
    case AssetDeliveryStatus.RequiresUserConfirmation:
        // Asset pack retrieval paused until the user accepts the
        // PlayAssetDelivery.ShowConfirmationDialog dialog.
    default:
        break;
}

// Returns true if status is AssetDeliveryStatus.Available or AssetDeliveryStatus.Failed.
bool done = request.IsDone;

// If AssetDeliveryStatus.Failed, find more info about the error.
AssetDeliveryErrorCode error = request.Error;

大型下載內容

大小超過 200 MB 的資產包只有在裝置連上 Wi-Fi 時才會自動下載。如果使用者未連上 Wi-Fi,PlayAssetPackRequest 狀態會設為 AssetDeliveryStatus.WaitingForWifi,下載作業也會暫停。在此情況下,請等到裝置連線至 Wi-Fi 後再繼續下載,或提示使用者允許透過行動網路連線下載資產包。

需要使用者確認

如果套件具有 AssetDeliveryStatus.RequiresUserConfirmation 狀態,使用者必須接受顯示 PlayAssetDelivery.ShowConfirmationDialog() 的對話方塊,下載作業才會繼續進行。如果 Play 無法辨識應用程式,就會出現這個狀態。請注意,在這種情況下呼叫 PlayAssetDelivery.ShowConfirmationDialog() 會導致應用程式更新。更新完成後,請再次要求素材資源。

if(request.Status == AssetDeliveryStatus.RequiresUserConfirmation
   || request.Status == AssetDeliveryStatus.WaitingForWifi) {
    var userConfirmationOperation = PlayAssetDelivery.ShowConfirmationDialog();
    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.
        case ConfirmationDialogResult.Accepted:
            // User accepted the confirmation dialog--an update will start.
        case ConfirmationDialogResult.Declined:
            // User canceled or declined the dialog. It can be shown again.
        default:
            break;
    }
}

取消要求 (僅限隨選)

如需在資產包下載之前取消要求,請在 PlayAssetPackRequest 物件上呼叫 AttemptCancel() 方法:

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

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

將資產載入記憶體

完成要求後,請使用下列其中一個函式將資產載入記憶體:

以非同步方式要求資產包

在多數情況下,您應使用協同程式,以非同步方式要求資產包並監控進度,如下所示:

private IEnumerator LoadAssetPackCoroutine(string assetPackName) {

    PlayAssetPackRequest request =
        PlayAssetDelivery.RetrieveAssetPackAsync(assetPackName);

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

            // 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(() => request.Status != AssetDeliveryStatus.WaitingForWifi);
        }

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

        yield return null;
    }

    if (request.Error != AssetDeliveryErrorCode.NoError) {
        // There was an error retrieving the pack. 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. Load the asset pack into memory.
    AssetBundleCreateRequest assetBundleCreateRequest = request.LoadAssetBundleAsync(path/to/exampleBundle);
    yield return assetBundleCreateRequest;
    AssetBundle assetBundle = assetBundleCreateRequest.assetBundle;

若要進一步瞭解如何處理錯誤,請參閱錯誤代碼清單。

其他 Play Core API 方法

以下列出一些其他您可能想在應用程式中使用的 API 方法。

擷取多個資產包

若要一次擷取多個資產包,請使用以下函式:

// assetPackNames is an array of strings corresponding to asset packs.
PlayAssetPackBatchRequest batchRequest = PlayAssetDelivery.RetrieveAssetPackBatchAsync(<IListstring> assetPackNames);

檢查 Dictionary 的狀態可以監控每項要求的狀態:

// Dictionary of AssetPackStates, with the asset pack name as the key.
Dictionary<string, PlayAssetPackRequest> requests = batchRequest.Requests;

// Returns true if all requests are complete.
bool requestComplete = batchRequest.IsDone;

檢查下載內容的大小

如要檢查資產包大小,請對 Google Play 發出非同步呼叫,並設定作業完成後的回呼方法:

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

您可以移除目前尚未載入記憶體的快速追蹤及隨選資產包。請進行以下非同步呼叫,並設定完成時的回呼方法:

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