使用 Addressables 來管理資源

使用 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修改為//本機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才正式運行。