使用 Addressables 來管理資源
一、安裝
打開Package Manager,在Unity Technologies的目錄下找到Addressables,更新或下載。

二、配置
依次打開Windows/Asset Management/Addressables/Groups菜單。
首次打開后會提示需要創建配置文件,點擊Create Addressables Settings。

Assets目錄下會生成AddressableAssetsData文件夾。

Ⅰ. 認識資源組(AssetGroups)與組策略(AssetGroups Schemas)概念
-
點擊
Manage Groups,返回剛才的Addressables Groups面板。此時已經有了兩個內置的組。 -
Built In Data存儲的是工程中Resources文件夾下的資源,隨App構建的場景資源,以及其他一些必要的資源等。 -
Default Local Group (Default)組存儲的是工程中被標記為Addressables的資源,假如不對其進行手動分組,則默認放在這個組中。 -
點擊
Create,可以指定模板來復制一個組或者創建一個全新的組。模板存儲在AddressableAssetsData/AssetGroupTemplates文件夾下,默認有一個名為Packed Assets的模板,新創建的組存儲在AddressableAssetsData/AssetGroups文件夾下。我們手動建立一個名為Remote的新組。

-
點擊
Remote組的Add Schema按鈕,添加Content Update Restriction和Content Packing & Loading策略。注意不要額外添加Resources and Built In Scene策略,這個已經由Built In Data組負責了。在
AssetGroups文件夾中有一個Schemas文件夾,存放着所有資源組的組策略序列化文件,命名規則為$"{組名}_{策略類型}"。

-
配置
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 Path和Load Path選項。-
LocalBuildPath本地構建路徑,當App發布時,會將這個路徑下的包拷貝到App的StreamingAssets里。 -
LocalLoadPath本地加載路徑,當App運行時,會從這個路徑下讀取資源包,一般也是在App的StreamingAssets里。Local___Path說明這個組中的資源包會包含在App的安裝包內。如果所有資源組都設置為Local,則這個App安裝包是 全資源包 (全資源包一般是設置為Can not Change Post Release的)。
-
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文件夾中呢。

點擊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不勾選即可,否則則需要將其勾選。

而Disable Catalog Update on Startup則決定是否在App啟動時自動更新catalog。
假如我們希望App啟動時將所有的更新包先行下載下來,則可以將其勾選,並在代碼中手動調用Addressables.InitializeAsync()、Addressables.CheckForCatalogUpdates()、Addressables.UpdateCatalogs()
等方法,獲取到需要更新的資源包列表,並對其依次進行手動更新。
假如我們希望在App運行中需要某個資源時才會去從遠程下載,則可以保持其不勾選的默認狀態。這種方式下Addressables系統會在App啟動時自動調用上述的一系列方法將本地catalog與遠程同步,但是並不會更新資源包。
三、資源管理
-
將資源導入工程中
-
此時
Inspector面板上新添加了一個Addressable選項。
將需要打包的資源勾選,出現一長串的資源路徑,這個路徑就是我們加載這個資源時所需要的路徑參數 -
點擊
Select按鈕,彈出資源組面板,剛添加的資源會位於默認組員組Default Local Group (Default)中。

-
如果能保證不沖突的話,我們也可以將這個路徑手動簡化,或者讓編輯器自動對其進行簡化。在資源組面板右鍵資源並點擊
Simplify Addressable Names,可以將復雜的路徑簡化為不帶類型后綴的文件名,方便使用。

-
也可以為資源指定標簽。將一系列資源指定為同一個標簽后,則可以在App運行時將其以按標簽加載的方式同時加載進來。比如我們將工程中的Lua腳本全部指定一個
Scripts的標簽等。
四、構建資源包
Ⅰ. 首次構建
首次構建資源包需要點擊資源組面板的Build/New Build/Default Build Script。

打包成功后在AddressableAssetsData文件夾下生成一個保存當前資源狀態的bin文件,這個文件是后續資源增刪改時用來與前一次打包做對比用的,並且它保存了至關重要的catalog文件信息。
同時在RemoteBuildPath位置生成catalog以及遠程資源包。

此時我們可以隨即進行App的構建。
每次構建資源包,都需要重新構建App。
如果希望更新資源包,不能使用Build/New Build方式。
Ⅱ. 更新包
將資源增刪改完畢之后,點擊Tools/Check for Content Update Restrictions。

選擇首次構建時生成的bin文件,彈出Content Update Preview面板,點擊Apply Changes。
我們這里的默認資源組的
Content Update Restriction選項設置的是Can not Change Post Release,因為本地資源已經跟隨App一同發布了,修改本地資源是沒有意義的,除非重新構建App。
編輯器替我們自動生成了新的遠程資源組,並將有變動的資源放了進去。
這里不要混淆 遠程資源組 和 Content Update Restriction 兩個概念,遠程資源組既可以標記為不可修改,也可以標記為可修改。
我們可以把這些資源放到其他遠程組里,也可以保持不變,這方面的策略應該顧及資源包的粒度,文件大小等,嚴格執行起來還是比較燒腦的。
資源組重新設定完畢后,點擊Build/Update a Previous Build,並再次選擇首次構建時生成的bin文件,以更新方式構建資源包。
切勿以
Build/New Build的方式構建,如果不小心點了,兩個解決方式,git reset --hard或者重新打包App……

此時打開遠程構建目錄(RemoteBuildPath),發現已經生成了由新的遠程資源組構建的資源包,而catalog文件的修改日期也已經發生了變化,說明雖然catalog文件名沒有發生變化,但是其內容已經更新了。
我這里截圖的catalog文件名發生了變化,是寫文測試的時候把之前的構建結果給刪了,實際上應該是不會發生變化的,請忽略。o_0

我們允許的流程是:構建資源包->構建App->以更新方式構建資源包->以更新方式構建資源包...
我們不允許的流程是:...->構建App->構建資源包...
原因是每次點擊Build/New Build/***,catalog文件名都會發生變化,而App只記錄上一次構建資源包時的catalog文件名,假如我們在構建App之后重新構建了資源包,則舊的App無法識別新的catalog,從而更新失敗。
五、部署
將RemoteBuildPath目錄下的文件上傳至服務器,包括資源包以及catalog文件,保證RemoteLoadPath可訪問即可。
實際部署還是比教程寫的要麻煩很多的。權限,跨域,負載能力,加密,防止各種網絡攻擊等,各種手段都要用上。我們僅僅是驗證技術路線,麻煩事就先不考慮啦。
如果僅僅是測試的話,其實編輯器還提供了一個Host功能

點擊Create/LocalHosting,新建一個服務端,指定端口,勾選Enable即可。
注意我們需要將
Profiles/RemoteLoadPath修改為http://本機IP或者localhost:端口號,默認Profiles的遠程加載地址多了一個[BuildTarget],這是訪問不到的。


六、加載
新建一個腳本,用來測試資源的加載。
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才正式運行。


