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代码/脚本自动化生成的主要内容,如果未能解决你的问题,请参考以下文章