使用 Addressables 來管理資源

liougouren_mo發表於2021-12-09

使用 Addressables 來管理資源

一、安裝

開啟Package Manager,在Unity Technologies的目錄下找到Addressables,更新或下載。

二、配置

依次開啟Windows/Asset Management/Addressables/Groups選單。

首次開啟後會提示需要建立配置檔案,點選Create Addressables Settings
建立組

Assets目錄下會生成AddressableAssetsData資料夾。
配置資料夾

Ⅰ. 認識資源組(AssetGroups)與組策略(AssetGroups Schemas)概念

  1. 點選Manage Groups,返回剛才的Addressables Groups皮膚。此時已經有了兩個內建的組。

  2. Built In Data儲存的是工程中Resources資料夾下的資源,隨App構建的場景資源,以及其他一些必要的資源等。

  3. Default Local Group (Default)組儲存的是工程中被標記為Addressables的資源,假如不對其進行手動分組,則預設放在這個組中。

  4. 點選Create,可以指定模板來複制一個組或者建立一個全新的組。模板儲存在AddressableAssetsData/AssetGroupTemplates資料夾下,預設有一個名為Packed Assets的模板,新建立的組儲存在AddressableAssetsData/AssetGroups資料夾下。我們手動建立一個名為Remote的新組。
    AssetGroups

  5. 點選Remote組的Add Schema按鈕,新增Content Update RestrictionContent Packing & Loading策略。注意不要額外新增Resources and Built In Scene策略,這個已經由Built In Data組負責了。

    AssetGroups資料夾中有一個Schemas資料夾,存放著所有資源組的組策略序列化檔案,命名規則為$"{組名}_{策略型別}"
    組策略

  6. 配置Remote組策略。

    • 修改在Content Update Restriction下的Update Restriction選項。

      Can Change Post Release的意思是,當前資源組進行最終構建時會完全覆蓋上一次構建結果(打包出的檔名與上一次不同,上一次的包徹底無效化,部署時可以直接刪掉),一般這種方式叫做 全量更新

      Can not Change Post Release則意味著,當前資源組構建時需要與上一次構建結果進行比對(Addressables Groups/Tools/Check for Content Update Restrictions),上一次構建出的包不發生變化,新構建的包則建立在舊包的基礎上(相同的資源儲存在舊包內,修改和新增的資源儲存在新包內,部署時需要將新包和舊包一起發到伺服器上),這種方式一般叫做 增量更新

    • 修改Content Packing & Loading下的Build PathLoad Path選項。

      • LocalBuildPath 本地構建路徑,當App釋出時,會將這個路徑下的包拷貝到App的StreamingAssets裡。

      • LocalLoadPath 本地載入路徑,當App執行時,會從這個路徑下讀取資源包,一般也是在App的StreamingAssets裡。

        Local___Path 說明這個組中的資源包會包含在App的安裝包內。如果所有資源組都設定為Local,則這個App安裝包是 全資源包 (全資源包一般是設定為Can not Change Post Release的)。

        Local___Path

      • RemoteBuildPath 遠端構建路徑,當構建資源包後,需要將這個路徑下的包拷貝到伺服器上。

      • RemoteLoadPath 遠端載入路徑,當App執行時,會從這個地址下載catalog,並與本地catalog對比來判斷是否需要更新資源。

      如果想對 全資源包 進行更新,可以點選Addressables Groups/Tools/Check for Content Update Restrictions,會自動生成差異化資源組,將這些資源組設定為遠端並構建部署,則可以將App內包含的舊資源進行覆蓋。

    • Advanced Options 是更精細化控制資源包構建與載入流程的選項,無特殊情況保持預設即可。需要注意的是Include in Build選項,可以控制當前資源包是否參與本次構建。

Ⅱ. 總體配置

AddressableAssetSettings負責整體配置資源包的構建引數。

設定資源包地址的Profiles

Profiles/Profiles In Use 控制了當前資源包路徑設定。

我們剛才對資源包的路徑進行了本地與遠端的設定,但是並沒有深入瞭解這些路徑是如何拼接而成的。比如為什麼本地資源包會在構建到StreamingAssets資料夾中呢。
Profiles

點選Manage Profiles,彈出上圖的Addressables Profiles皮膚,之前我們設定的本地路徑與遠端路徑就是在這裡定義的。總體上路徑由固定的字串、中括號與花括號組成,中括號內包含了釋出時編輯器所確定下來的變數,比如執行平臺等;花括號則包含了App執行時所獲取的變數,也可以在程式碼內手動指定一個自定義的變數。

這就解釋了為什麼我們將資源包指定為Local___Path時,最終會存在於App的StreamingAssets資料夾中,是因為[UnityEngine.AddressableAssets.Addressables.BuildPath]這個地址就是編輯器釋出App時所指定目錄下的StreamingAssets資料夾地址,而{UnityEngine.AddressableAssets.Addressables.RuntimePath}這個地址則是App執行時的StreamingAssets資料夾地址。

我們也可以新建一個測試用的Profile和一個釋出用的Profile,只需要把RemoteLoadPath中的http地址指向測試用伺服器地址和正式版伺服器地址即可。

設定是否需要遠端更新

假如我們的App不需要進行遠端資源載入和更新,則保持Content Updata/Build Remote Catalog不勾選即可,否則則需要將其勾選。
Update

Disable Catalog Update on Startup則決定是否在App啟動時自動更新catalog。
假如我們希望App啟動時將所有的更新包先行下載下來,則可以將其勾選,並在程式碼中手動呼叫
Addressables.InitializeAsync()
Addressables.CheckForCatalogUpdates()
Addressables.UpdateCatalogs()
等方法,獲取到需要更新的資源包列表,並對其依次進行手動更新。

假如我們希望在App執行中需要某個資源時才會去從遠端下載,則可以保持其不勾選的預設狀態。這種方式下Addressables系統會在App啟動時自動呼叫上述的一系列方法將本地catalog與遠端同步,但是並不會更新資源包。

三、資源管理

  1. 將資源匯入工程中

  2. 此時Inspector皮膚上新新增了一個Addressable選項。
    將需要打包的資源勾選,出現一長串的資源路徑,這個路徑就是我們載入這個資源時所需要的路徑引數

  3. 點選Select按鈕,彈出資源組皮膚,剛新增的資源會位於預設組員組Default Local Group (Default)中。
    Assets

  4. 如果能保證不衝突的話,我們也可以將這個路徑手動簡化,或者讓編輯器自動對其進行簡化。在資源組皮膚右鍵資源並點選Simplify Addressable Names,可以將複雜的路徑簡化為不帶型別字尾的檔名,方便使用。
    Simply

  5. 也可以為資源指定標籤。將一系列資源指定為同一個標籤後,則可以在App執行時將其以按標籤載入的方式同時載入進來。比如我們將工程中的Lua指令碼全部指定一個Scripts的標籤等。

四、構建資源包

Ⅰ. 首次構建

首次構建資源包需要點選資源組皮膚的Build/New Build/Default Build Script
Build

打包成功後在AddressableAssetsData資料夾下生成一個儲存當前資源狀態的bin檔案,這個檔案是後續資源增刪改時用來與前一次打包做對比用的,並且它儲存了至關重要的catalog檔案資訊。

同時在RemoteBuildPath位置生成catalog以及遠端資源包。
RemoteBuildPath

此時我們可以隨即進行App的構建。

每次構建資源包,都需要重新構建App。

如果希望更新資源包,不能使用Build/New Build方式。

Ⅱ. 更新包

將資源增刪改完畢之後,點選Tools/Check for Content Update Restrictions
Update Assets

選擇首次構建時生成的bin檔案,彈出Content Update Preview皮膚,點選Apply Changes

我們這裡的預設資源組的Content Update Restriction選項設定的是Can not Change Post Release,因為本地資源已經跟隨App一同釋出了,修改本地資源是沒有意義的,除非重新構建App。
Content Update Preview

編輯器替我們自動生成了新的遠端資源組,並將有變動的資源放了進去。

這裡不要混淆 遠端資源組Content Update Restriction 兩個概念,遠端資源組既可以標記為不可修改,也可以標記為可修改。
Content Update Groups

我們可以把這些資源放到其他遠端組裡,也可以保持不變,這方面的策略應該顧及資源包的粒度,檔案大小等,嚴格執行起來還是比較燒腦的。

資源組重新設定完畢後,點選Build/Update a Previous Build,並再次選擇首次構建時生成的bin檔案,以更新方式構建資源包。

切勿以Build/New Build的方式構建,如果不小心點了,兩個解決方式,git reset --hard或者重新打包App……

Update Build

此時開啟遠端構建目錄(RemoteBuildPath),發現已經生成了由新的遠端資源組構建的資源包,而catalog檔案的修改日期也已經發生了變化,說明雖然catalog檔名沒有發生變化,但是其內容已經更新了。

我這裡截圖的catalog檔名發生了變化,是寫文測試的時候把之前的構建結果給刪了,實際上應該是不會發生變化的,請忽略。o_0

New Build Result

我們允許的流程是:構建資源包->構建App->以更新方式構建資源包->以更新方式構建資源包...

我們不允許的流程是:...->構建App->構建資源包...

原因是每次點選Build/New Build/***,catalog檔名都會發生變化,而App只記錄上一次構建資源包時的catalog檔名,假如我們在構建App之後重新構建了資源包,則舊的App無法識別新的catalog,從而更新失敗。

五、部署

RemoteBuildPath目錄下的檔案上傳至伺服器,包括資源包以及catalog檔案,保證RemoteLoadPath可訪問即可。

實際部署還是比教程寫的要麻煩很多的。許可權,跨域,負載能力,加密,防止各種網路攻擊等,各種手段都要用上。我們僅僅是驗證技術路線,麻煩事就先不考慮啦。

如果僅僅是測試的話,其實編輯器還提供了一個Host功能
Hosting

點選Create/LocalHosting,新建一個服務端,指定埠,勾選Enable即可。

注意我們需要將Profiles/RemoteLoadPath修改為http://本機IP或者localhost:埠號,預設Profiles的遠端載入地址多了一個[BuildTarget],這是訪問不到的。

Hosting 2

Hosting 3

六、載入

新建一個指令碼,用來測試資源的載入。

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.UI;

public class SwitchSprites : MonoBehaviour
{
    [SerializeField] private RawImage _emojiImage;
    [SerializeField] private Text _infoText;

    private int _indicator;

    private AsyncOperationHandle<IList<Texture>> _operation;

    private async void Start()
    {
        _operation = Addressables.LoadAssetsAsync<Texture>("emoji", null);
        await _operation.Task;

        _infoText.text = "Emoji Load Completed";

        GetComponent<Button>().onClick.AddListener(() =>
        {
            _indicator++;
            if (_indicator >= _operation.Result.Count)
            {
                _indicator = 0;
            }

            _emojiImage.texture = _operation.Result[_indicator];
        });
    }

    private void OnDestroy()
    {
        Addressables.Release(_operation);
    }
}

這裡一上來直接就可以Addressables.LoadAssetsAsync<Texture>(),是因為我測試的時候沒有勾選AddressableAssetSettings/Content Update /Disable Catalog Update on Startup,所以App在載入時自動更新了catalog,而資源包下載則類似於Lazy 模式,即用即下載(快取中如果有匹配的資源包則直接載入)。真正使用這套系統時,一般會採用勤快一點的模式,先更新必要的資源包,讀條一波,然後App才正式執行。

相關文章