Odin Inspector 系列教程 ---【小工具】UGUI節(jié)點(diǎn)收集器(UINodeCollection)

本工具是基于Odin制作,方便查找對(duì)應(yīng)面板的UGUI組件钓株,并提供對(duì)應(yīng)腳本創(chuàng)建实牡、組件賦值、常規(guī)沖突檢測(cè)等機(jī)制轴合。
方便快捷易上手创坞,筆者已經(jīng)做好注釋,易于魔改受葛。(例如生成Lua文件等)题涨。

選擇要添加的類型

添加對(duì)應(yīng)類型的節(jié)點(diǎn)偎谁,不符合類型會(huì)報(bào)錯(cuò)

選擇UI分部類對(duì)應(yīng)的腳本文件

點(diǎn)擊【導(dǎo)出對(duì)應(yīng)分部類】按鈕,節(jié)點(diǎn)出現(xiàn)重名會(huì)有提示

點(diǎn)擊【為組件進(jìn)行賦值】按鈕進(jìn)行綁定


示例完整腳本

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("導(dǎo)出對(duì)應(yīng)的分部類", ButtonSizes.Large)]
    private void ExportPartialScript()
    {
        if (string.IsNullOrEmpty(filePath))
        {
            UnityEditor.EditorUtility.DisplayDialog("警告", "沒(méi)有選擇需要生成的分部類", "確定");
            return;
        }
        if (IsHaveRepetitiveName()) return;

        //獲取對(duì)應(yīng)的類名稱
        var layerArray = filePath.Split(Path.AltDirectorySeparatorChar);
        className = layerArray[layerArray.Length - 1].Split('.')[0];
        //生成絕對(duì)路徑
        string path = filePath.Replace(className, className + "Extention");

        StringBuilder content = new StringBuilder();
        //添加對(duì)應(yīng)的注釋
        content.AppendLine(CreateAnnotation().ToString());
        //添加命名空間
        content.Append(AdditionalNamespacesToString());
        //添加內(nèi)容
        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("為組件進(jìn)行賦值", 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("http://開發(fā)者:海瀾");
        stringBuilder.AppendLine("http://描述:快速查找對(duì)應(yīng)UI組件節(jié)點(diǎn)及代碼綁定");
        stringBuilder.AppendLine("http://腳本創(chuàng)建時(shí)間:" + DateTime.Now);
        stringBuilder.AppendLine(
@"http://********************************
//本腳本為自動(dòng)創(chuàng)建【切勿更改】
//********************************");
        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("發(fā)現(xiàn)重復(fù)元素", content, "確定");
            return true;
        }
        return false;
    }

    private string GetRepetitiveName(UINodeGroup uINodeGroup)
    {
        var nodes = uINodeGroup.nodes;
        //先去重復(fù)
        //然后查找對(duì)應(yīng)Key的個(gè)數(shù) 纲堵,大于1就是重復(fù)
        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 += $"重復(fù)元素:{group[i].Element} 重復(fù)的個(gè)數(shù)為:{group[i].Counter}";
        }
        return content;
    }


#endif

    #endregion

    [Serializable]
    private class UINodeGroup
    {
#pragma warning disable CS0649
        [LabelText("節(jié)點(diǎn)類型")]
        [ValueDropdown("GetFilteredTypeList")]
        public string typeName;
#pragma warning restore CS0649

        [ChildGameObjectsOnly]
        [ValidateInput("ValidateComponent", "填對(duì)對(duì)應(yīng)的組件非指定類型", InfoMessageType.Error)]
        public List<UnityEngine.Component> nodes = new List<UnityEngine.Component>();

        #region 編輯器

#if UNITY_EDITOR
        /// <summary>
        /// typeName類型過(guò)濾
        /// </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>
        /// 對(duì)添加節(jié)點(diǎn)類型的驗(yàn)證
        /// </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
    }

}

更多教程內(nèi)容詳見(jiàn):革命性Unity 編輯器擴(kuò)展工具 --- Odin Inspector 系列教程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末巡雨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子席函,更是在濱河造成了極大的恐慌铐望,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茂附,死亡現(xiàn)場(chǎng)離奇詭異正蛙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)营曼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門乒验,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蒂阱,你說(shuō)我怎么就攤上這事锻全。” “怎么了录煤?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵鳄厌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我辐赞,道長(zhǎng)部翘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任响委,我火速辦了婚禮新思,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赘风。我一直安慰自己夹囚,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布邀窃。 她就那樣靜靜地躺著荸哟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瞬捕。 梳的紋絲不亂的頭發(fā)上鞍历,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音肪虎,去河邊找鬼劣砍。 笑死,一個(gè)胖子當(dāng)著我的面吹牛扇救,可吹牛的內(nèi)容都是我干的刑枝。 我是一名探鬼主播香嗓,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼装畅!你這毒婦竟也來(lái)了靠娱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤掠兄,失蹤者是張志新(化名)和其女友劉穎像云,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚂夕,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苫费,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了双抽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闲礼,死狀恐怖牍汹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柬泽,我是刑警寧澤慎菲,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站锨并,受9級(jí)特大地震影響露该,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜第煮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一解幼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧包警,春花似錦撵摆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至壹瘟,卻和暖如春鲫剿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稻轨。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工灵莲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澄者。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓笆呆,卻偏偏與公主長(zhǎng)得像请琳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子赠幕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345