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

 

posted @ 2022-02-28 15:38  tiancaiKG  阅读(3113)  评论(0编辑  收藏  举报