0
点赞
收藏
分享

微信扫一扫

使用Asset Graph来构建资源工作流

田妞的读书笔记 2022-01-13 阅读 118
Unity

之前在做资源导入管理的时候,一直都是使用各种各样AssetImporter来分别处理,这其实也挺好的,但是我之前遇到的问题其中有一个就是假如我想修改某个资源的属性,这个属性和Importer中约束的不同的话,又会被改回去,后来就变成不停的细分更多的文件夹。

另外Asset Graph不单单能用作导入的处理,还可以用作资源的批量处理,通过使用它来实现自己的资源工作流。

首先安装通过Package Manager安装插件

然后右键Create -Asset Graph 创建一个Graph

下面我们实现一个简单的功能,将一个文件夹下的图片自动设为Sprite 常见的UI图片操作

第一个Load From Directory 节点用于选取文件夹

可以用来选定一个文件夹,作为导入器的时候需要将Respond To Asset Change勾选上

下面一个节点Split By Fiter 用于资源过滤,因为修改成Sprite的资源一定是Texture ,我们需要过滤只剩下Texture

这里还可以使用正则表达式对文件名进行额外的过滤

下面对所有过滤出来的Texture进行进一步处理 ,这里用到Overwirte Import Settings

当新建这个节点的时候需要选择类型,选定Texture

选定之后这个就会成为我们的模版文件,所有的都会和这个一样

你可以像正常图片一样,选择它的所有属性

这个配置就存在AssetGraph的SavedSettings下面,你之后也可以直接修改这个文件来修改资源模版

另外还有一个需要说明的事,AssetGraph在使用过程中会生成大量的Cache,在你使用版本控制的时候,需要将这些Cache文件夹添加到ignore中

与此同时打开Use As Postprocessor 作为资源后处理,这个时候所有的导入的资源都会遵循模版设置

当资源目录里的资源发生变动的时候就会批量修改资源与模版保持一致

另外需要说明一下这个Respond To Asset Change的作用 加入你想要让你的修改也生效的话 就取消勾选,如果一直遵循模版的设置勾选上

这就是一个简单的资源导入器的配置,类似的也可以方便的实现音频模型等的预处理


下面在看看如何利用Asset Graph来生成预制体的过程 包含了批量处理以及资源生成

上面是一个根据模型生成对应的材质处理,

先创建一个生成资源节点

生成的资源有三种路径可以选择 分别是缓存目录 选择的目录,和源文件目录,这里我选择源资源目录。

下面来创建自定义生成脚本,来对我们的逻辑进行自定义处理Window=》AssetGraph=>....

接下来就可以在之前的Generate Asset下选择到创建的脚本了

下面看看源码中生成的核心逻辑

Package/AssetGraph/Editor/System/Node/Buildin/AssetGenerator.cs这个脚本

需要注意的是IAssetGenerator接口中GenerateAsset返回false的时候就会抛出异常,中断生成,所以需要直接return false

[CustomAssetGenerator("Create Role Material", "v0.1", 1)]
    public class RoleMaterialGenerator : IAssetGenerator
    {
        [SerializeField] private string shaderName = "URP/Character";
        public bool CanGenerateAsset(AssetReference asset)
        {
            return true;
        }

        public bool GenerateAsset(AssetReference asset, string generateAssetPath)
        {
            bool generated = false;

            if (!string.IsNullOrEmpty(generateAssetPath))
            {
                var tempMat = AssetDatabase.LoadAssetAtPath<Material>(generateAssetPath);//已经生成过了 不在生成了
                if (tempMat != null)
                {
                    return true;
                }
                Material material = new Material(Shader.Find(shaderName));
                AssetDatabase.CreateAsset(material, generateAssetPath);
                generated = true;
            }

            //移动材质

            return generated;
        }

        public string GetAssetExtension(AssetReference asset)
        {
            return "_Mat.mat";
        }

        public Type GetAssetType(AssetReference asset)
        {
            return typeof(Material);
        }
        public void OnInspectorGUI(Action onValueChanged)
        {
            EditorGUILayout.TextField("Material Shader:", shaderName);
        }

        public void OnValidate(){}

        public static string FileNameWithoutSuffix(string name)
        {
            if (name == null)
            {
                return null;
            }

            int endIndex = name.LastIndexOf('.');
            if (endIndex > 0)
            {
                return name.Substring(0, endIndex);
            }
            return name;
        }

预制体也是差不多的操作

var go = AssetDatabase.LoadAssetAtPath<GameObject>(asset.importFrom);
GameObject objSource = (GameObject)PrefabUtility.InstantiatePrefab(go);
//其他变体处理
GameObject obj = PrefabUtility.SaveAsPrefabAsset(objSource, generateAssetPath);
UnityEngine.MonoBehaviour.DestroyImmediate(objSource);

我这里用原先的模型生成一个新的预制体变体,当需要换模型的时候直接更换模型就好了,其他就不需要做额外的操作,这里在这个预制体变体上在进行额外的处理,比如挂在脚本,添加动画等等。

当脚本都写完后点击执行Execute,就会进行处理了


上面就介绍了两种Asset Graph的简单实用,当然它能做的还不止于此,我用的还比较浅显,用它可以可视化节点化的定制资源工作流,尤其是资源的生成,往往模型很多 ,挂脚本,处理大量的相同操作,使用它能够带来巨大的便利

//

Addressable资源管理

1, 使用Asset Graph为资源设置Addressable和group

添加结点如上图,总共设置6个Group, 在Set Asset Address节点,设置Path Match Pattern如下:

将路径 Assets/AssetsPackage/ 替换为“”, 对Lua 目录添加了Label节点,本来想通过Label来一次性读取所有Lua文件进行预加载,但是读取后不能获取单个Lua文件的原始路径,因此,此处Label节点没什么用。

2,配置Addressables

选择对应平台,点击Execute,将按Asset Graph配置设置好资源的Addressables。

打开Addressables Groups窗口,配置好的Group如下:

3,配置Addressables Profiles

打开Addressables Profiles窗口,添加如下Profile:

本文在window上测试,后续操作都选择 production_win 这个profile, RemoteLoadPath:为:http://60.1.1.12/et/win/, 注意此处配置支持两种语法:

  • [BuildTarget] 方括号里可以使用Unity的全局变量
  • {Addressable.AddressableConfig.server_android_url} 大括号中可以直接引用类中的静态变量 ,(用此配置语法可以实现程序中动态修改资源更新地址)

4,配置静态资源和动态资源

目前我希望所有资源都打入到初始包中,首先配置AddressableAssetSettings:

注意设置此3处,

  • Profile In Use: production_win 前面配置的profile
  • Disable Catalog Update on Startup : 应用启动时不自动更新Catalog
  • Build Remote Catalog : 第一个Build时生成Remote Catalog, 不勾选无法增量更新

5. 配置Group

  • Build Path : 生成到本地的路径,在最终打包时自动复制到StreamingAssets目录
  • Load Path: 本地加载时的路径
  • Update Restriction: Cannot Change Post Release 由于我们想将初始group设置所静态资源打包到最终程序包中增量更新,因此此group生成的bundle打包后将不能修改。如果想全量更新,设选择Can Change Post Release ,当然,前面的Build Path和Load Path都修改为Remote配置。

6. 首次打包

在Addressables Groups窗口中,选择: Build / New Build / Default Build Script , 执行后:

在上面目录中将生成 Catlog 文件,里面包含所有本地资源的Addressables Path.

将上面的文件放入资源服务器中,游戏启动时去更新此catlog文件来检测是否需要更新资源 。

7. 资源Patch

当项目中资源发生修改后,

  • 在AssetGraph窗口中重新Execute, 将对新资源进行Addressable配置,
  • 然后在Addressables Groups窗口中,点击 Tools/Check for Content Update Restriction , 选择6 打包生成的bin

此时将自动分析出修改的文件,点击Apply Changes ,将生成新的group, 新group配置如下 :

最后,点击 Build / Update a Previous Build , 在ServerData目录中将更新catlog文件和生成新的patch bundle

将此目录中的文件放到资源服务器中,游戏启动时将检查并更新bundle.

Nice Lua框架中资源更新代码如下:

IEnumerator checkUpdate()
    {
        var start = DateTime.Now;


        var initHandle =Addressables.InitializeAsync();
        yield return initHandle;

        var a = Addressables.RuntimePath;
        var checkHandle = Addressables.CheckForCatalogUpdates(false);
        yield return checkHandle;
        Logger.Log(string.Format("CheckIfNeededUpdate use {0}ms", (DateTime.Now - start).Milliseconds));
        Logger.Log($"catalog count: {checkHandle.Result.Count} === check status: {checkHandle.Status}");
        if(checkHandle.Status == AsyncOperationStatus.Succeeded)
        {
            List<string> catalogs = checkHandle.Result;
            if (catalogs != null && catalogs.Count > 0)
            {
               
                needUpdateRes = true;

                statusText.text = "正在更新资源...";
                slider.normalizedValue = 0f;
                slider.gameObject.SetActive(true);

               
                start = DateTime.Now;
                AsyncOperationHandle<List<IResourceLocator>> updateHandle = Addressables.UpdateCatalogs(catalogs, false);
                yield return updateHandle;

                var locators = updateHandle.Result;
                Logger.Log($"locator count: {locators.Count}");

                foreach(var v in locators)
                {
                    List<object> keys = new List<object>();
                    keys.AddRange(v.Keys);

                    var sizeHandle = Addressables.GetDownloadSizeAsync(keys);
                    yield return sizeHandle;

                    long size = sizeHandle.Result;
                    Logger.Log($"download size:{size}");

                    if(size > 0)
                    {
                        UINoticeTip.Instance.ShowOneButtonTip("更新提示", $"本次更新大小:{size}", "确定", null);
                        yield return UINoticeTip.Instance.WaitForResponse();


                        var downloadHandle = Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union);
                        while (!downloadHandle.IsDone)
                        {
                            float percentage = downloadHandle.PercentComplete;
                            Logger.Log($"download pregress: {percentage}");
                            slider.normalizedValue = percentage;

                            yield return null;
                        }
                        Addressables.Release(downloadHandle);
                    }
                }

                Logger.Log(string.Format("UpdateFinish use {0}ms", (DateTime.Now - start).Milliseconds));
                yield return UpdateFinish();

                Addressables.Release(updateHandle);
            }

            Addressables.Release(checkHandle);
        }
        

        needUpdateRes = false;

        ChannelManager.instance.resVersion = "112";
        ChannelManager.instance.appVersion = "1.0";
        yield return StartGame();

    }

Unity-AssetGraph 使用小结

1.创建一个AssetGeneratorScript

2. 加入自己喜欢的逻辑

3. 添加自己创建的方法

4. 创建CUI buildassetbundle.bat

5. 找到刚创建好的文件,Show in Explorer

6. 以上步骤都做完,我们可以关掉Unity了

7. CMD 环节

记录:

问题 1 :在生成prefab的时候,想把图片TextureImportSetting压缩格式,sRGB等属性也一起设置了,然后发现了override platform 不生效,然后搜了一下,assetgraph override import setting doesn't work

https://github.com/Unity-Technologies/AssetGraph/issues/155

看了一下代码,发现生成了一份本地setting.asset,这个本地文件是一直都没有勾上的。

我们只需要找到本地生成的setting.asset文件,然后勾上override就好了~

举报

相关推荐

0 条评论