Odin Inspector 系列教程 ---【小工具】UGUI节点收集器(UINodeCollection)

本工具是基于Odin制作,方便查找对应面板的UGUI组件,并提供对应脚本创建、组件赋值、常规冲突检测等机制。
方便快捷易上手,笔者已经做好注释,易于魔改。(例如生成Lua文件等)。

选择要添加的类型

添加对应类型的节点,不符合类型会报错

选择UI分部类对应的脚本文件

点击【导出对应分部类】按钮,节点出现重名会有提示

点击【为组件进行赋值】按钮进行绑定


示例完整脚本

using Sirenix.OdinInspector;
using Sirenix.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;

public class UINodeCollection : MonoBehaviour
{
    [SerializeField]
    [ListDrawerSettings(DraggableItems = false, Expanded = false)]
    private List<UINodeGroup> uINodes = new List<UINodeGroup>();


    #region 编辑器

#if UNITY_EDITOR
    const string k_Tab = "    ";
    public string className;

#pragma warning disable CS0649
    [ShowInInspector]
    [LabelText("选择需要生成的分部类")]
    [FilePath(Extensions = "cs, lua", AbsolutePath = true)]
    private string filePath;
#pragma warning restore CS0649

    [Button("导出对应的分部类", ButtonSizes.Large)]
    private void ExportPartialScript()
    {
        if (string.IsNullOrEmpty(filePath))
        {
            UnityEditor.EditorUtility.DisplayDialog("警告", "没有选择需要生成的分部类", "确定");
            return;
        }
        if (IsHaveRepetitiveName()) return;

        //获取对应的类名称
        var layerArray = filePath.Split(Path.AltDirectorySeparatorChar);
        className = layerArray[layerArray.Length - 1].Split('.')[0];
        //生成绝对路径
        string path = filePath.Replace(className, className + "Extention");

        StringBuilder content = new StringBuilder();
        //添加对应的注释
        content.AppendLine(CreateAnnotation().ToString());
        //添加命名空间
        content.Append(AdditionalNamespacesToString());
        //添加内容
        content.AppendLine("public partial class " + className + "\r\n" + "{\r\n");
        for (int i = 0; i < uINodes.Count; i++)
        {
            content.AppendLine(CreateOneGroup(uINodes[i]));
        }
        content.AppendLine("}\r\n");

        CreateScript(path, content);
        UnityEditor.AssetDatabase.Refresh();
        Debug.Log("生成代码完毕");
    }

    [Button("为组件进行赋值", ButtonSizes.Large)]
    private void SetComponentValue()
    {
        string nameSpace = "Assembly-CSharp";
        var assembly = Assembly.Load(nameSpace);
        var tempType = assembly.GetType(className);
        var type = transform.GetComponent(tempType);
        var fieldInfos = type.GetType().GetFields();

        for (int i = 0; i < fieldInfos.Length; i++)
        {
            var item = fieldInfos[i];
            var targetType = item.FieldType;

            var temp = uINodes.Where(a => a.typeName == targetType.Name)//筛选类型
                .SelectMany(a => a.nodes)
                .Where(t => t.name.Replace(" ", "") == item.Name)
                .FirstOrDefault();
            var targetValue = temp.gameObject.GetComponent(targetType);
            item.SetValue(type, targetValue);
        }
    }

    private StringBuilder CreateAnnotation()
    {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.AppendLine("//开发者:海澜");
        stringBuilder.AppendLine("//描述:快速查找对应UI组件节点及代码绑定");
        stringBuilder.AppendLine("//脚本创建时间:" + DateTime.Now);
        stringBuilder.AppendLine(
@"//********************************
//本脚本为自动创建【切勿更改】
//********************************");
        return stringBuilder;
    }
    private string AdditionalNamespacesToString()
    {
        return
            "using System;\r\n" +
            "using System.Collections;\r\n" +
            "using System.Collections.Generic;\r\n" +
            "using System.IO;\r\n" +
            "using System.Linq;\r\n" +
            "using UnityEngine;\r\n" +
            "using UnityEngine.UI;\r\n" +
            "using Sirenix.OdinInspector; \r\n" +
            "using TMPro; \r\n" +
            "\r\n";
    }
    private string CreateOneGroup(UINodeGroup uINodeGroup)
    {
        string temp = "";
        var nodes = uINodeGroup.nodes;
        string typeName = uINodeGroup.typeName;
        for (int i = 0; i < nodes.Count; i++)
        {
            temp += k_Tab + $"public {typeName} {nodes[i].name.Replace(" ", "")};\r\n";
        }
        return temp;
    }
    private void CreateScript(string fileName, StringBuilder content)
    {
        string path = fileName;

        using (FileStream fs = new FileStream(path, FileMode.Create))
        {
            using (StreamWriter writer = new StreamWriter(fs, System.Text.Encoding.UTF8))
            {
                writer.Write(content.ToString());
            }
        }
    }

    private bool IsHaveRepetitiveName()
    {
        string content = "";
        for (int i = 0; i < uINodes.Count; i++)
        {
            content += GetRepetitiveName(uINodes[i]);
        }
        if (!string.IsNullOrEmpty(content))
        {
            UnityEditor.EditorUtility.DisplayDialog("发现重复元素", content, "确定");
            return true;
        }
        return false;
    }

    private string GetRepetitiveName(UINodeGroup uINodeGroup)
    {
        var nodes = uINodeGroup.nodes;
        //先去重复
        //然后查找对应Key的个数 ,大于1就是重复
        List<string> strList = new List<string>();
        for (int i = 0; i < nodes.Count; i++)
        {
            strList.Add(nodes[i].name);
        }

        var group = strList.GroupBy(str => str).Where(g => g.Count() > 1).Select(y => new { Element = y.Key, Counter = y.Count() }).ToList();
        string content = "";
        for (int i = 0; i < group.Count; i++)
        {
            content += $"重复元素:{group[i].Element} 重复的个数为:{group[i].Counter}";
        }
        return content;
    }


#endif

    #endregion

    [Serializable]
    private class UINodeGroup
    {
#pragma warning disable CS0649
        [LabelText("节点类型")]
        [ValueDropdown("GetFilteredTypeList")]
        public string typeName;
#pragma warning restore CS0649

        [ChildGameObjectsOnly]
        [ValidateInput("ValidateComponent", "填对对应的组件非指定类型", InfoMessageType.Error)]
        public List<UnityEngine.Component> nodes = new List<UnityEngine.Component>();

        #region 编辑器

#if UNITY_EDITOR
        /// <summary>
        /// typeName类型过滤
        /// </summary>
        /// <returns></returns>
        private IEnumerable<string> GetFilteredTypeList()
        {
            var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
                .Where(t => !t.IsAbstract)
                .Where(t => !t.IsGenericTypeDefinition)
                .Where(t => typeof(Component).IsAssignableFrom(t))
                .Where(t => t.Namespace == "UnityEngine.UI")
                .Where(t => t.IsPublic);

            types = types.AppendWith(typeof(Transform));

            var typeNames = types.Select(t => t.Name);
            return typeNames;
        }

        /// <summary>
        /// 对添加节点类型的验证
        /// </summary>
        /// <param name="nodes"></param>
        /// <returns></returns>
        private bool ValidateComponent(List<UnityEngine.Component> nodes)
        {
            if (nodes.Count <= 0 || typeName == "Transform") return true;

            string nameSpace = "UnityEngine.UI";
            var node = nodes[nodes.Count - 1];

            var assembly = Assembly.Load(nameSpace);
            var tempType = assembly.GetType(nameSpace + "." + typeName);
            var tempComponent = node.GetComponent(tempType);

            return tempComponent != null;
        }
#endif

        #endregion
    }

}

更多教程内容详见:革命性Unity 编辑器扩展工具 --- Odin Inspector 系列教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342