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

Posted 凉_开果

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity工具类扩展——UGUI代码/脚本自动化生成相关的知识,希望对你有一定的参考价值。

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

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

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

效果图

####简单的 一层

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


代码部分解析

####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))] 每个需要重新自定义面板都需要打上这个特性标签

效果大概这样


####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 喜欢给个星星

Unity编辑器扩展——自动生成UI界面脚本

一:前言

对于面板赋值或Find绑定UI组件,我们可以使用一种工具化的方式去自动生成代码并绑定对象,增加效率
分为logic和view,view层是UI界面上的组件,每次都会自动生成并覆盖,logic层是逻辑


二:使用


例如一个UI界面,我们只需要做成预制体并在Project下右键预制体,选择AutoGen/Create View则会自动生成view和logic两个脚本,logic是我们要编写逻辑的脚本,view是每次都会自动生成并覆盖的脚本


三:说明

——以下几个路径都是可以自定义的(view和logic生成的路径、view和logic模版文件的路径)


——可以自定义忽略的组件类型列表


——只会生成对象名带下划线的


四:代码实现 

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Text;
using System;

public class AutoGenCode

    //logic层代码路径
    const string LogicDir = "Assets/AutoGen/Logic";
    //view层代码路径
    const string ViewDir = "Assets/AutoGen/View";
    //logic层模版文件路径
    const string LogicTempletePath = "Assets/AutoGen/LogicTemplete.txt";
    //view层模版文件路径
    const string ViewTempletePath = "Assets/AutoGen/ViewTemplete.txt";

    //命名空间模板
    const string NameSpaceTemplete = "using 0;";
    //字段模板
    const string FieldTemplete = "public 0 1;\\t";
    //方法模板
    const string MethodTemplete = "0 = gameObject.transform.Find(\\"1\\").GetComponent<2>();\\t\\t";

    /// <summary>
    /// 忽略的组件类型列表
    /// </summary>
    static List<Type> IgnoreComponentTypeList = new List<Type>()
    
        typeof(CanvasRenderer),
        typeof(RectTransform),
    ;

    [MenuItem("Assets/AutoGen/Create View", priority = 0)]
    static void CreateLogicAndView()
    
        GameObject go = Selection.activeGameObject;
        //判断是否是prefab
        if (PrefabUtility.GetPrefabAssetType(go) != PrefabAssetType.Regular)
        
            Debug.LogWarning("选择的不是预制体,选择的对象:" + go.name);
            return;
        
        if (!Directory.Exists(ViewDir))
        
            Directory.CreateDirectory(ViewDir);
        
        if (!Directory.Exists(LogicDir))
        
            Directory.CreateDirectory(LogicDir);
        

        string className = go.name + "View";
        StringBuilder fieldContent = new StringBuilder();
        StringBuilder methodContent = new StringBuilder();
        StringBuilder nameSpaceContent = new StringBuilder();
        nameSpaceContent.AppendLine(NameSpaceTemplete.Replace("0", "UnityEngine"));//必须有UnityEngine命名空间

        string logicTempleteContent = File.ReadAllText(LogicTempletePath, Encoding.UTF8);
        string viewTempleteContent = File.ReadAllText(ViewTempletePath, Encoding.UTF8);
        string logicPath = LogicDir + "/" + go.name + "Logic.cs";
        string viewPath = ViewDir + "/" + go.name + "View.cs";

        List<string> tempNameSpaceList = new List<string>();

        //计算所有子物体组件数据
        List<ComponentInfo> infoList = new List<ComponentInfo>();
        CalcComponentInfo("", go.transform, infoList);
        foreach (var tempInfo in infoList)
        
            //字段
            string tempFieldStr = FieldTemplete.Replace("0", tempInfo.TypeStr);
            tempFieldStr = tempFieldStr.Replace("1", tempInfo.FieldName);
            fieldContent.AppendLine(tempFieldStr);

            //绑定方法
            string tempMethodStr = MethodTemplete.Replace("0", tempInfo.FieldName);
            tempMethodStr = tempMethodStr.Replace("1", tempInfo.Path);
            tempMethodStr = tempMethodStr.Replace("2", tempInfo.TypeStr);
            methodContent.AppendLine(tempMethodStr);

            //命名空间
            if (!tempNameSpaceList.Contains(tempInfo.NameSpace))
            
                string tempNameSpaceStr = NameSpaceTemplete.Replace("0", tempInfo.NameSpace);
                tempNameSpaceList.Add(tempInfo.NameSpace);
                nameSpaceContent.AppendLine(tempNameSpaceStr);
            
        

        //logic层脚本
        if (!File.Exists(logicPath))
        
            using (StreamWriter sw = new StreamWriter(logicPath))
            
                string content = logicTempleteContent;
                content = content.Replace("#CLASSNAME#", className);
                sw.Write(content);
                sw.Close();
            
        
        //view层脚本
        using (StreamWriter sw = new StreamWriter(viewPath))
        
            string content = viewTempleteContent;
            content = content.Replace("#NAMESPACE#", nameSpaceContent.ToString());
            content = content.Replace("#CLASSNAME#", className);
            content = content.Replace("#FIELD_BIND#", fieldContent.ToString());
            content = content.Replace("#METHOD_BIND#", methodContent.ToString());
            sw.Write(content);
            sw.Close();
        

        AssetDatabase.Refresh();
    

    /// <summary>
    /// 计算所有子物体组件数据
    /// </summary>
    static void CalcComponentInfo(string path, Transform child, List<ComponentInfo> infoList)
    
        bool isRoot = string.IsNullOrEmpty(path);
        if (!isRoot
            && IsVaildField(child.name))
        
            var componentList = child.GetComponents<Component>();
            foreach (var tempComponent in componentList)
            
                ComponentInfo info = new ComponentInfo()
                
                    Path = path,
                    go = child.gameObject,
                    NameSpace = tempComponent.GetType().Namespace,
                    TypeStr = tempComponent.GetType().Name,
                ;
                if (!HaveSameComponentInfo(info, infoList)
                    && !IgnoreComponentTypeList.Contains(tempComponent.GetType()))
                
                    infoList.Add(info);
                
            
        

        foreach (Transform tempTrans in child.transform)
        
            CalcComponentInfo(isRoot ? tempTrans.name : path + "/" + tempTrans.name, tempTrans.transform, infoList);
        
    

    /// <summary>
    /// 是否为合法的字段名
    /// </summary>
    static bool IsVaildField(string goName)
    
        if (goName.Contains("_"))
        
            if (int.TryParse(goName[0].ToString(), out _))
            
                Debug.LogWarning("字段名不能以数字开头:, goName :" + goName);
                return false;
            
            return true;
        
        return false;
    

    /// <summary>
    /// 是否有相同的组件数据
    /// </summary>
    static bool HaveSameComponentInfo(ComponentInfo info, List<ComponentInfo> infoList)
    
        foreach (var tempInfo in infoList)
        
            if (tempInfo.FieldName == info.FieldName)
            
                Debug.LogWarning("子物体名重复:, goName :" + info.go.name);
                return true;
            
        
        return false;
    


/// <summary>
/// 组件数据
/// </summary>
public class ComponentInfo

    public string Path;
    public GameObject go;
    public string NameSpace;
    public string TypeStr;
    public string FieldName
    
        get
        
            return $"go.name_TypeStr";
        
    

以上是关于Unity工具类扩展——UGUI代码/脚本自动化生成的主要内容,如果未能解决你的问题,请参考以下文章

unity 原生UGUI代码扩展

unity 原生UGUI代码扩展

Unity编辑器扩展——自动生成UI界面脚本

Unity编辑器扩展——自动生成UI界面脚本

unity ugui 怎么制作下拉动态刷新

小技巧Unity UGUI 中使用脚本激活按钮onclick事件