Unity基础入门分享程序员Unity技术分享

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

2019-04-09  本文已影响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 欢迎进来灌水=。=

上一篇下一篇

猜你喜欢

热点阅读