美文网首页Unity基础入门分享程序员Unity技术分享
Unity工具类扩展——UGUI代码自动化生成

Unity工具类扩展——UGUI代码自动化生成

作者: 懒_开果 | 来源:发表于2019-04-09 17:50 被阅读241次

    【为什么要做自动化工具】

    工具类的创建是为了解决实际问题或者优化既有流程,我们来先看看一些项目里面经常遇到的问题。

    下面这个工具就是可以直接创建一个功能的基础脚本类,就不用每次去复制上次的代码了。然后再帮我们把那些乱七八糟又数不胜数的按钮、文字、图片组件都自动生成在脚本里面,然后自己去关联好引用,一下就能节省好多重复的活。

    效果图

    简单的 一层

    qqw12.gif

    复杂点的 管理Panel 子管理Panel 孙管理

    image

    代码部分解析

    1 枚举类型 UIMarkType 对应指定的类型 UIType是默认自有的类型可以自己拓展

    public enum UIMarkType{
    DefaultUnityElement = 0,
    Element = 1
      }
    public enum UIType
    {
    Transform = 0,
    Image = 1,
    RawImage = 2,
    Button = 3,
    Toggle = 4,
    Slider = 5,
    Scrollbar = 6,
    Dropdown = 7,
    InputField = 8,
    ScrollRect = 9,
    Text = 10,
    ToggleGroup = 11,
    Canvas = 12,
    RectTransform = 13,
    Animator = 14,
    IMark = 15,
    

    }

    2 接口IMark 主要用于拓展
    public interface IMark
    {
    string ComponentName { get; }
    
    
    UIMarkType GetUIMarkType();
    
    }
    

    ComponentName获取要创建的类型字符
    GetUIMarkType() 获取当前UIMarkType

    3 UIMark标签类 用于标记生成什么样

    public class UIMark : MonoBehaviour, IMark
    {
    [Header("指定类型")]
    public UIMarkType MarkType = UIMarkType.DefaultUnityElement;
    [Header("当前选择创建属性类型")]
    public UIType CreateType;
    
    
    [Header("创建脚本类名")]
    public string CustomComponentName;
    
    public UIMarkType GetUIMarkType()
    {
        return MarkType;
    }
    
    public virtual string ComponentName
    {
        get
        {
    
            if (MarkType == UIMarkType.DefaultUnityElement)
            {
                if (CreateType == UIType.IMark)
                {
                    return GetComponents<IMark>().First(v=>v.GetType()!=this.GetType()).ComponentName;
                }
                return CreateType.ToString();
            }
    
    
            return CustomComponentName;
        }
    }
    
    public void InitCreateType()
    {
        if (MarkType == UIMarkType.DefaultUnityElement)
        {
    
            var TempMark = GetComponents<IMark>().Where(v => v.GetType() != this.GetType());
    
            if (TempMark.Count()>0)
                CreateType = UIType.IMark;
    
           else  if (null != GetComponent<ScrollRect>())
                CreateType = UIType.ScrollRect;
            else if (null != GetComponent<InputField>())
                CreateType = UIType.InputField;
            else if (null != GetComponent<Text>())
                CreateType = UIType.Text;
            else if (null != GetComponent<Button>())
                CreateType = UIType.Button;
            else if (null != GetComponent<RawImage>())
                CreateType = UIType.RawImage;
            else if (null != GetComponent<Toggle>())
                CreateType = UIType.Toggle;
            else if (null != GetComponent<Slider>())
                CreateType = UIType.Slider;
            else if (null != GetComponent<Scrollbar>())
                CreateType = UIType.Scrollbar;
            else if (null != GetComponent<Image>())
                CreateType = UIType.Image;
            else if (null != GetComponent<ToggleGroup>())
                CreateType = UIType.ToggleGroup;
    
            else if (null != GetComponent<Animator>())
                CreateType = UIType.Animator;
    
            else if (null != GetComponent<Canvas>())
                CreateType = UIType.Canvas;
    
            else if (null != GetComponent<RectTransform>())
                CreateType = UIType.RectTransform;
    
            else if (null != GetComponent<Transform>())
                CreateType = UIType.Transform;
    
        }
    }
    
    }
    

    实现了了IMark
    [Header("xxx")]  在Inspector面板上给定义的字段的上一行加段描述
    InitCreateType()是用来识别当前适合什么自有的类型 如果太多组件可能会错就要Inspector面板改了

    4 CustomEditorUIMarkEditor类 用于UIMark类的自定义Inspector面板

    [CanEditMultipleObjects, CustomEditor(typeof(UIMark))]
    public class CustomEditorUIMarkEditor : Editor
    {
    public override void OnInspectorGUI()
    {
    
        EditorGUILayout.PropertyField(this.serializedObject.FindProperty("MarkType"));
    
        if (this.serializedObject.FindProperty("MarkType").enumValueIndex == 1)
        {
            EditorGUILayout.PropertyField(this.serializedObject.FindProperty("CustomComponentName"));
        }
        else
        {
            EditorGUILayout.PropertyField(this.serializedObject.FindProperty("CreateType"));
        }
    
    
        // 应用属性修改
        this.serializedObject.ApplyModifiedProperties();
    }
    
    }
    

    EditorGUILayout.PropertyField 搜索自定义的类里面的属性名称 然后绘制
    特性[CanEditMultipleObjects, CustomEditor(typeof(UIMark))] 每个需要重新自定义面板都需要打上这个特性标签

    效果大概这样


    image.png
    image.png

    5 AddUIMark类 右键添加按钮UIMark的

    public class AddUIMark
    {
    [MenuItem("GameObject/KGUI/AddUIMark", priority = 0)]
    static void AddUIMarkMenu()
        {
        GameObject[] obj = Selection.gameObjects;
    
        for (int i = 0; i < obj.Length; i++)
        {
            
            if (!obj[i].GetComponent<UIMark>())
            {
                obj[i].AddComponent<UIMark>().InitCreateType();
            }
            else
            {
                obj[i].GetComponent<UIMark>().InitCreateType();
            }
    
        }
    }
    }
    

    MenuItem 按钮的定义 想要在Hierarchy视图右键的话 路径就要GameObject/下的 然后要选层级 默认层级是不出现在右键的

    6 GeneratorData 就一些静态数据

    public class GeneratorData
    {
    #region UIClass
    
    public static string UIClass =
       @"using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    using System;
    public class #类名# : MonoBehaviour
    {
    
    //替换标签
    
    #region UIModule
    
    #成员#
    
    #endregion
    
     public void Awake()
    {
        InitFind();
    }
    
     public void InitFind()
    {
    #查找#
    }
    
    }
    ";
        #endregion
    #region ElementClass
    
      public static string ElementClass =
     @"using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.EventSystems;
    using System;
    public class #类名# : MonoBehaviour
    {
    //这是子类
    //替换标签
    
    #region UIModule
    
    #成员#
    
    #endregion
    
     public void Awake()
    {
        InitFind();
    }
    
     public void InitFind()
    {
    #查找#
    }
    
    }
    ";
        #endregion
        public static Type GetType(string name)
        {
       // Type type=null;
        var AssemblyCSharp = AppDomain.CurrentDomain.GetAssemblies().First(v =>     v.FullName.StartsWith("Assembly-CSharp,"));
            return AssemblyCSharp.GetType(name);
        }
    }
    

    var AssemblyCSharp 是获取所有程序集筛选Assembly-CSharp 这个集

    7 UICodeGenerator 一键生成添加脚本

    public class UICodeGenerator
    {
        private static Action ff;
        public static GameObject gg;
        public static string tt="fff";
        [MenuItem("GameObject/KGUI/生成脚本", priority = 0)]
        public static void UIScriptGenerator()
        {
    
             if (EditorPrefs.GetBool("ScriptGenerator"))
            {
                  return;
            }
        GameObject[] selectobjs = Selection.gameObjects;
    
        foreach (GameObject go in selectobjs)
        {
            Generator(go);        
        }
    }
    
    
    public static void ScriptGenerator(GameObject go,string UIClass, string Classname="")
    {
        //选择的物体
        GameObject selectobj = go;
    
        //物体的子物体
        List<Transform> childList = selectobj.GetComponentsInChildren<Transform>(true).ToList();
        Debug.Log(childList);
        List<Transform> ElementList = childList.Where(v => { return v.GetComponent<UIMark>() && v.GetComponent<UIMark>().MarkType == UIMarkType.Element&&v!= go.transform; }).ToList();
    
        ElementList.ForEach(v =>
        {
            v.GetComponentsInChildren<Transform>(true).Where(Obj => {return Obj.GetComponent<UIMark>()&& Obj != v;}).ToList().ForEach(remove =>
                {
                    childList.Remove(remove);
                });
        });
        if (childList.Contains(go.transform))
        {
            childList.Remove(go.transform);
        }
        //  List<Transform> childList = new List<Transform>(_transforms);
    
        //UI需要查询的物体
        var mainNode = childList.Where(v => v.GetComponent<UIMark>());
    
        var nodePathList = new Dictionary<string, string>();
    
        string ClassName = Classname == "" ? go.name : Classname;
    
        //循环得到物体路径
        foreach (Transform node in mainNode)
        {
            Transform tempNode = node;
            string nodePath = "/" + tempNode.name;
    
            while (tempNode != go.transform)
            {
                tempNode = tempNode.parent;
    
                if (tempNode != go.transform)
                {
                    int index = nodePath.IndexOf('/');
    
                    nodePath = nodePath.Insert(index, "/" + tempNode.name);
                }
              
            }
            nodePath = nodePath.Substring(1);
            nodePathList.Add(node.name, nodePath);
        }
    
        //成员变量字符串
        string memberstring = "";
        //查询代码字符串
        string loadedcontant = "";
    
        foreach (Transform itemtran in mainNode)
        {
            //每个类的名字 字符
            string typeStr = itemtran.GetComponent<UIMark>().ComponentName;
    
            // Debug.Log();
            memberstring += "public " + typeStr + " " + itemtran.gameObject.name + " = null;\r\n\t";
            //物体的路劲寻找 字符
            loadedcontant += "\t\t" + itemtran.name + " = " + "gameObject.transform.Find(\"" + nodePathList[itemtran.name] + "\").GetComponent<" + typeStr + ">();\r\n";
        }
    
    
        string scriptPath = Application.dataPath + "/Scripts/" + ClassName + ".cs";
    
    
        string classStr = "";
    
        gg = selectobj;
        tt = selectobj.name;
    
        if (!Directory.Exists(Application.dataPath + "/Scripts"))
        {
            Directory.CreateDirectory(Application.dataPath + "/Scripts");
        }
        if (File.Exists(scriptPath))
        {
            FileStream classfile = new FileStream(scriptPath, FileMode.Open);
            StreamReader read = new StreamReader(classfile);
            classStr = read.ReadToEnd();
            read.Close();
            classfile.Close();
            File.Delete(scriptPath);
    
            //分割 区分手写和 生成的
            string splitStr = "//替换标签";
            string unchangeStr = Regex.Split(classStr, splitStr)[0];
            string changeStr = Regex.Split(GeneratorData.UIClass, splitStr)[1];
    
            StringBuilder build = new StringBuilder();
            build.Append(unchangeStr);
            build.Append(splitStr);
            build.Append(changeStr);
            classStr = build.ToString();
        }
        else
        {
            classStr =UIClass;
        }
    
        classStr = classStr.Replace("#类名#", ClassName);
        classStr = classStr.Replace("#查找#", loadedcontant);
        classStr = classStr.Replace("#成员#", memberstring);
    
    
        FileStream file = new FileStream(scriptPath, FileMode.CreateNew);
    
        StreamWriter fileW = new StreamWriter(file, System.Text.Encoding.UTF8);
        fileW.Write(classStr);
        fileW.Flush();
        fileW.Close();
        file.Close();
    
    
        Debug.Log("创建脚本 " + Application.dataPath + "/Scripts/" + ClassName + ".cs 成功!");
    }
    
    public static void Generator(GameObject go)
    {
    
        ScriptGenerator(go, GeneratorData.UIClass);
    
    
    
        go.GetComponentsInChildren<UIMark>(true).Where(v=>v.MarkType==UIMarkType.Element).ToList().ForEach(v=> 
        {
    
            ScriptGenerator(v.gameObject, GeneratorData.ElementClass,v.CustomComponentName);
        });
    
        EditorPrefs.SetBool("ScriptGenerator", true);
    
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
    
        
    
    }
    
    [UnityEditor.Callbacks.DidReloadScripts]
    public static void AddScript()
    {
        if (!EditorPrefs.GetBool("ScriptGenerator"))
        {
            return;          
        }
        EditorPrefs.SetBool("ScriptGenerator", false);
        AssetDatabase.Refresh();
    
    
    
    
        Selection.gameObjects.ToList().ForEach(v =>
        {
    
    
            if (!v.GetComponent(GeneratorData.GetType(v.name)))
                v.AddComponent(GeneratorData.GetType(v.name));
    
    
            v.GetComponentsInChildren<UIMark>(true).Where(element => element.MarkType == UIMarkType.Element).ToList().ForEach(elementMark =>
            {
                if (!elementMark.GetComponent(GeneratorData.GetType(elementMark.CustomComponentName)))
                {
                    elementMark.gameObject.AddComponent(GeneratorData.GetType(elementMark.CustomComponentName));
                    UnityEngine.Object.DestroyImmediate(elementMark);
    
    
                }
    
            });
    
        });
    
        Debug.Log(tt+12344);
      
    }
    

    }

    EditorPrefs.Set/GetBool 用于面板存取数据的

    UIScriptGenerator()会遍历当前选择的物体进行生成脚本
    Generator() 处理生成脚本的逻辑

    ScriptGenerator() 指定物体为他生成相应的脚本

    先筛选出符合条件的属性的 mainNode
    循环得到 物体的路径 生成路径字符
    判断是否含有该文件夹没有则创建

     if (!Directory.Exists(Application.dataPath + "/Scripts"))
            {
                Directory.CreateDirectory(Application.dataPath + "/Scripts");
            }
    

    通过File.Exists判断是否有该脚本 有的就只是修改脚本没有就创建

    AddScript() 代码生成后 的添加操作

    特性[UnityEditor.Callbacks.DidReloadScripts]用于脚本改动的回调

    好了 以上就是 整个过程


    工程地址 https://github.com/LKaiGuo/KGScriptGenerator 喜欢给个星星

    u3d萌新QQ群844087555 欢迎进来灌水=。=

    相关文章

      网友评论

        本文标题:Unity工具类扩展——UGUI代码自动化生成

        本文链接:https://www.haomeiwen.com/subject/ssfiiqtx.html