隨着 Unity 的 Addressables 逐漸完善, 已經可以替代其它的打包加載卸載等工具的功能了, 今天做了一下測試, 有那么幾個挺好的地方 :
1. AssetReferenceT<T> 引用的對象, 也正確參與 Addressables 相關加載卸載邏輯.
2. 有一些貼心設計比如場景如果加入到 Addressables Groups 中的話, 如果被 Build Settings 引用到, 會自動取消引用 :
場景會自動取消引用, 如果強行勾選 Build Settings 里的場景, 會自動刪除 Addressables Groups 中的引用. 這樣就保證了場景不會在打包時重復了.
不過場景打包的資源仍然跟 Addressables 是分開管理的.
比如下面這樣, Test001 勾選為默認場景, 場景里有一張貼圖 :
而在場景中又通過代碼加載同樣一張貼圖, 就會變成這樣資源重復了 :
說明場景的貼圖是被 Resources 那邊管理的, 這張貼圖只能通過 Resources.UnloadUnusedAssets(); 來卸載.
所以場景還是用一個空場景作為起始場景, 再通過 Addressables 加載其它場景就行了.
5. 以前 AssetBundle 會出現的默認材質 Shader 重復編譯問題, 猜測應該沒問題, 測試看看 :
看來一般狀況下不管怎樣打包和分包, 都不會造成材質 Shader 的重復編譯了.
-------------------------------
看了一下生成出來的 AssetBundle 包, 感覺並不是很正確, 這里的場景是引用了一個 Prefab, 而所有的材質和貼圖引用都是通過 Prefab 來的, 如果我只對場景進行打包的話, 得到的是下面這樣的結果 :
如果把場景和 Prefab 都進行打包的話, 得到下面的結果 :
可見場景的大小並沒有改變, 並且 Prefab 的大小也是跟場景差不多, 這就說明場景和 Prefab 都把引用資源給打包了.......
查了一下, 原來是創建材質 Group 的時候代碼添加錯誤導致的, 通過代碼創建需要制定相應的控制器, 這里記錄一下 :
public static List<Type> SchemaTypes { get { return new List<Type>() { typeof( UnityEditor.AddressableAssets.Settings.GroupSchemas.BundledAssetGroupSchema), typeof( UnityEditor.AddressableAssets.Settings.GroupSchemas.ContentUpdateGroupSchema), }; } } private static void AddNewAsset(UnityEngine.Object asset, string groupName) { var settings = AddressableAssetSettingsDefaultObject.Settings; if(settings) { var assetPath = AssetDatabase.GetAssetPath(asset); var group = settings.FindGroup(groupName) ?? settings.CreateGroup(groupName, false, false, false, new List<AddressableAssetGroupSchema> { settings.DefaultGroup.Schemas[0] }, SchemaTypes.ToArray()); if(group) { var guid = AssetDatabase.AssetPathToGUID(assetPath); var entry = settings.CreateOrMoveEntry(guid, group); entry.SetAddress(assetPath, true); } } }
PS : 創建資源 Group 可以創建很多個, 或者在一個Group 里面選擇分開打包, 下面的都得到差不多的包, 材質全是獨立的 Assetbundle 包, 不知道在加載時有沒有性能差別.
對於不把材質作為顯式打包的設置的話, 只引用場景和 Prefab, 狀況就回到前面資源重復的狀態了 :
可以猜想到重復引用的資源, 仍然會被重復打包. 那么加載出來的資源, 就必然又會重復了 :
材質重復
圖片也重復了
重新把材質引用到 Addressables Group 里面來, 就正常了 :
所以目前來說, 它還不是一個手動就能維護的打包系統, 仍然需去厲遍所有資源的引用數量, 重復引用資源仍然要自己控制生成相應 Groups, 要不然資源加載的控制也是無從說起的. 目前它的好處就是把 Shader 的編譯問題給解決了, 這在以前是個老大難問題......
而且通過查看文件大小, 可以看出 Addressables 把多余的變體給刪除了 :
同樣的材質 Addressables 和普通 BuildPipeline.BuildAssetBundles 打包出來的大小天差地別, 應該是在標准材質的變體上
PS : 不過之前測試時用了兩個 Canvas 和 AssetReferenceT<Texture> 引用了同一張圖片, 可是圖片沒有進行 Addressables 的顯式引用, 不過加載的時候也沒有產生資源重復的情況, 估計是對於圖片和 Mesh 這類的唯一性資源, Addressables 已經自己做了處理了吧
又錯了, 之前測試的時候是把兩個 Canvas 放在同一個 Group 里面了, 而且 AssetReferenceT<Texture> 這個序列化會自動把引用的圖片加到 Addressables 里面去, 造成了圖片資源不會重復加載的假象 :
當兩個 UI 處於不同的包的時候, 並且沒有圖片的顯式引用的話, 仍然會在打包和加載的時候造成重復.
再來看看圖集的使用情況, 最近很奇怪 Unity 右鍵菜單找不到創建 SpriteAtlas 的操作.......
創建了兩個 spriteatlas, 因為圖片沒有加入 Addressables 所以是灰色的, 加載通過先加載 SpriteAtlas 然后再獲取下面的圖片, 跟我們的常識一樣, 能夠正常合批.
可是如果把其中的一些圖片加入到 Group 里面來, 就容易出問題了 :
加入前
加入后
應該是圖片獨立出去之后, 作為普通圖片存在了, 並且非PO2所以圖片不壓縮, 就很大.
如果獨立的圖片跟 SpriteAtlas 同一個包, 它又能夠作為圖集的一部分了 :
尺寸也恢復了, 所以如果有需求, 需要把圖片和圖集放在同一個包里. 不過即使這樣通過 SpriteAtlas 加載出來的圖片, 跟直接通過資源加載的圖片, 仍然是不同的, 這個需要記住 :
Addressables.LoadAssetAsync<SpriteAtlas>("Assets/Atlas/SA2.spriteatlas").Completed += (_atlas) => { imgs[0].sprite = _atlas.Result.GetSprite("bg_icon"); Debug.Log("spriteatlas : " + imgs[0].sprite.name); Addressables.LoadAssetAsync<Sprite>("Assets/Sprites/SP1/bg_icon.png").Completed += (_sp) => { imgs[1].sprite = _sp.Result; Debug.Log("sprite : " + imgs[1].sprite.name); Debug.Log(imgs[0].sprite == imgs[1].sprite); }; Addressables.LoadAssetAsync<Sprite>("Assets/Sprites/SP1/ic_pos.png").Completed += (_sp) => { imgs[2].sprite = _sp.Result; Debug.Log("sprite : " + imgs[2].sprite.name); }; };
結果沒有什么意外, 不過從 SpriteAtlas 來的圖片, 是一個克隆, 可能每次通過接口獲取都會造成克隆.
意外的是通過資源加載的 Sprite, 它們都能夠正確合批, 比如上面的三張圖, 能夠在一次 draw 中繪制......根據觀察, 通過上面的資源接口加載出來的 Sprite, 也是依賴於 SpriteAtlas 的, 自動加載了圖集. 所以目前看來, 圖集都是能正確合批的.
還有一個問題, 以前 AssetBundleName 設置的時候, 如果場景的設置跟資源的設置是一樣的話, 打包會報錯, 告訴你場景不能跟一般資源打在一個包里 :
在 Addressables Group 里面是可以設置到一個 Group 里面的, 並且打包設置為 Pack Together 的話, 也是不會報錯的 :
資源和場景設置在了同一個 Group 中一起打包
生成的包查看一下, 發現它自動把場景和資源給分開了, build 出來兩個 AssetBundle 包 :
生成了一個 _assets_all 的包和 _scenes_all 的包, 自動幫你分好包了.
---------- 一些補充 ------------
1. 在 Group 編輯的時候可以看到一般新加資源會自動命名為帶了擴展名的路徑, 這個就保證了它的唯一性, 不過在不斷修改的過程中比如 Assets/A.mat 文件位置變為 Assets/Materials/A.mat 之后, 它在 Addressables 里面的 Key 也是不會變的, 還是老樣子, 好處是代碼不需要變動.
問題是如果又加入一個 Assets/A.mat 資源, 就會有兩個相同的名稱資源了 :
就會造成混亂, 其實還是永遠保持路徑為 Key 才是最准確的, 雖然會因為資源位置移動造成代碼需要修改的情況.
PS : 在這個面板右鍵有一個快捷方式簡化 Key 名稱, 可是沒有還原 Key 為文件路徑的方法, 有點搞笑 :
只能用代碼去改回 :
var settings = AddressableAssetSettingsDefaultObject.Settings; if(settings) { foreach(var group in settings.groups) { foreach(var entry in group.entries) { entry.SetAddress(entry.AssetPath, true); } } }
2. 文件夾也能設置為 Addressables 資源, 估計跟 Resources 一樣能夠讀取整個文件夾 :
測試了一下, 不行...
文件夾拖入 Group 只能展現文件夾, 如果里面的資源沒有勾選 Addressable 的話, 是灰色的, 如果勾選了的話就變成跟 Assets/Materials 同級的了, 不能放到里面去了......
不管在任何設置下, 都不能通過 Assets/Materials 作為 Key 來加載文件夾下的資源 :
Addressables.LoadAssetsAsync<Material>("Assets/Materials", (_obj) => { }).Completed += (_list)=> { var mats = _list.Result; foreach (var mat in mats) { Debug.Log(mat.name); } };
會報錯......既然不能加載, 為啥要讓文件夾也能放到 Addressables 系統里, 理解不能.
3. 回調
找了半天才找到, AssetBundle Build 的回調 :
UnityEditor.AddressableAssets.Build.BuildScript.buildCompleted
現在 External Tools 里面把源碼 package 之類的都勾上, 可以在工程中查看源碼
4. 出現了打包經典BUG :
Could not find a part of the path ""
以前打包也會有這個問題, 設置包名的時候文件名過長會出現這個問題, 一般情況下如果太長又需要它的包名是唯一的話, 直接用 GUID 代替包名即可 :
var guid = AssetDatabase.AssetPathToGUID(assetPath); if(assetPath.Length > 80) { var setPath = System.IO.Path.GetFileName(assetPath) + "_" + guid; Debug.LogError("路徑過長 : " + assetPath + "\n修改為 : " + setPath); assetPath = setPath; }
打包選項可以選擇添加一些頭頭尾尾的比如哈希值, 所以包名會比想象的要長, 特別是處於根目錄下的包, 你需要添加 GUID 作為唯一性, 打包系統也會添加頭尾給你比如 :
自己添加 GUID ->
打包系統添加頭部和尾部哈希值 ->
最終長度就會變得很長, 超過 windows 的限制...
====================================================================
一些使用上的問題.
1. 批量設置資源的時候, 如果每個資源的 postEvent 都是 true 的話, 會非常慢, 一般沒有特殊需求可以都不進行 event 通知.
2. 在設置完之后刷一次 event 就可以刷新 Addressables Groups 面板顯示了 :
settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryModified, null, true);