Unity工具类扩展——UGUI代码自动化生成
【为什么要做自动化工具】
工具类的创建是为了解决实际问题或者优化既有流程,我们来先看看一些项目里面经常遇到的问题。
下面这个工具就是可以直接创建一个功能的基础脚本类,就不用每次去复制上次的代码了。然后再帮我们把那些乱七八糟又数不胜数的按钮、文字、图片组件都自动生成在脚本里面,然后自己去关联好引用,一下就能节省好多重复的活。
效果图
简单的 一层
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]用于脚本改动的回调