本篇基本上是官方演示的東西,介紹一下如何快速使用Addressables實現資源熱更。為了能夠熱更資源,你需要有一個資源服務器使你能下載資源。
一、資源准備
首先打開Unity編輯器菜單 -> Window -> Asset Management -> Addressable Assets。最好將它固定到Editor里面,便於隨時操作。然后隨便制作一個prefab備用。
將該prefab拖拽到Addressables窗口的"Default Local Group"組中,結果如下圖:

為了便於載入,使用如圖的右鍵菜單項將其名字進行簡化,它變成了成 "MyRemotePrefab"。

左鍵點擊以選中 "Default Local Group"這個條目,在其Inspector窗口中我們可以看到其各種Schema的屬性:

要保證這個Group中一定有BundledAssetGroupSchema與ContentUpdateGroupSchema,其中的資源才能正常被熱更。按如下的方式設置各種屬性:
1.將ContentUpdateGroupSchema中的StaticContent設置為false;
2.將BundledAssetGroupSchema中的BuildPath選中為 "RemoteBuildPath";
3.將BundledAssetGroupSchema中的LoadPath選中為"RemoteLoadPath";
4.檢查AssetBundleProviderType,確認其值是 AssetBundleProvider;
5.此時它的LoadPath應該還不是你實際上的資源服上的路徑。選中Project面板中的 Assets/AddressableAssetsData/AddressableAssetSettings, 在"Profiles"這一節,編輯ProfileEntries中的RemoteLoadPath條目,改成你的資源服務器地址:

如果想要修改其他條目的路徑,也可以一並修改。
二、准備測試場景
創建一個MonoBehaviour,名為LoadPrefab,代碼如下:
using UnityEngine; using UnityEngine.AddressableAssets; public class LoadPrefab : MonoBehaviour { public string address; // Start is called before the first frame update void Start() { Load(); } void Load() { Addressables.LoadAsset<GameObject>(address).Completed += (op) => { if (op.Status == UnityEngine.ResourceManagement.AsyncOperationStatus.Succeeded) { Debug.Log(op.Result as GameObject); var go = Instantiate(op.Result as GameObject, Vector3.zero, Quaternion.identity, transform); go.transform.localPosition = Vector3.zero; } }; } }
在場景中創建一個空的GameObject,然后將該MonoBehaviour添加到它上面。由於我創建的是一個UI,因此我創建了一個Canvas而不是空的GO.
我們將該Component中的Address修改為 MyRemotePrefab ,使之能被載入。
三、添加日志輸出
這部分只是為了查看日志,可以直接跳過。
1.在Assets文件夾中創建一個文本文件,改名為 csc.rsp。該文本文件中的內容為 "-define:ADDRESSABLES_LOG_ALL"(不包括引號)。其作用是讓Addressables輸出的日志更詳細。
2.在Packages(注意它不在Assets這個文件夾中)中找到Addressables System,選中它其中任意一個cs文件,然后Reimport之。這是為了讓Unity重新編譯以使第1步創建的 預編譯指令文件生效。
四、打包
在Addressables窗口中,點擊Build菜單執行"Build Player Content"操作:

然后將RemoteBuildPath(如果沒有修改過,它應該是 ProjectDir/ServerData/StandaloneWindows64)中生成的文件全部上傳到你的資源服務器對應路徑中。
最后,打一個游戲包出來。
運行之后,可以看到在稍微等待零點幾秒后,加載出了對應的Prefab(一個UI界面):

哦豁,由於我忘了給右上角的文本添加錨點,導致它顯示不全。
五、熱更新
將原有的UI進行修改,給文本添加了錨點並將它們移到右上角,更換了背景圖片的顏色。
然后,我們要使用Addressables窗口的Build/Build For Content Update來進行更新資源的打包。

結果它彈出了一個文件選擇窗口,讓我們選擇 Build Data File。這個文件實際上是上次打包時生成的、用於記錄那時候資源相關的各種狀態數據的一個文件。它存放在 ProjectDir/Library/com.unity.addressables 這個文件夾之中。
我們選中addressables_content_state.bin 這個文件,點擊 "打開(O)"按鈕以確定。
之后Unity進行打包,完成后可以看到在 RemoteBuildPath文件夾下已經生成了新的文件:

我們將這些新文件上傳到資源服務器,然后重新啟動程序,可以看到錨點問題已經修正了,背景顏色也已被修改,說明我們已經用新的prefab替換掉了老的:

六、一些細節問題
1.一般來說,我們是想要將資源先放在StreamingAssets中,等到發現有問題了才進行熱更。但是如果你的所有Group都是本地的,Addresssables將不會嘗試到服務器上下載最新的數據,也就無法進行資源更新了。因此這里只能一開始就將資源弄成Remote的。
2.Unity更新該資源的過程,簡單來說就是:
首先,實際上所有的Address都有一個對應的實際路徑,通過加載時傳入的Address,它經過一系列操作將它轉換為一個實際地址,然后從該地址加載得到對應的對象。對於默認的情況,這個Address與實際路徑對應的表是一個叫做catalog的JSON文件。
如果你的資源需要更新,則"Build For Content Update"之后,你將得到與原先同名但是已被修改過的catalog文件,然后你將該catalog文件(以及其他文件)上傳到服務器上。
程序啟動后,將首先加載catalog表。而實際上此時本地的catalog表已經是過時的了。程序如何知道它本地的catalog是過時的呢?
對於每一個Group,如果它上面有BundledAssetGroupSchema,則你可以看到其中有一個屬性是"Content Catalog Load Order",默認是0。其注釋說
///The order in which the content catalog from this group will be loaded. Usually remote catalogs are tried first and would have a lower value. If this value is less than 0, no catalog will be generated.
好像比較費解。經過實際測試,看起來是這么回事:由於每個Group都可以對應一套加載路徑,那么catalog的加載路徑究竟使用哪個Group的LoadPath呢?這個Order就指定了嘗試的次序。它將從該值為0的Group之LoadPath開始嘗試,如果加載不成功就去找Order為1的……(存疑,也可能是這樣的邏輯:按照加載的次序,依次加載每個catalog,並且合並加載出的所有內容)在加載catalog的時候,它會先比較該catalog的hash值與Application.persistentData中保存的catalog.hash值,如果不一致則說明該catalog較本地緩存中的要新,則使用這個版本並且將hash和json文件都保存到本地緩存中;否則就直接使用本地緩存中的數據。關於這個嘗試列表,它是存放在 StreamingAssets/com.unity.addressables/settings.json 這個文件中的。
由於catalog已被更新,因此實際加載的時候,程序就能知道本地的資源已經過期,將自動去指定的資源服務器上下載並得到對應的新內容。
3.這個Addressables系統擴展性是很高的,熟悉了之后可以玩出很多花樣;比起自己從Unity原有的資源加載與管理方式基礎上弄一整套資源管理系統,Unity官方的方案應該是更穩定、擴展性更高、適用性更廣的。當然現在不管是文檔還是功能都不太完善,要實際應用還可以稍微等等。據論壇官方人員說,將在數周內放出一個示例項目,希望到時候能看到完整的、有趣的應用。
接下來應該是詳細地看一看它的實現代碼,把細節弄清楚,然后看怎樣去進行自己的擴展。
