Unity Addressables 的使用相關


  隨着 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);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM