UIScriptGenerator

This commit is contained in:
Alex-Rachel
2025-03-15 00:42:23 +08:00
parent be3b6eb8f2
commit 9bffcce30e
8 changed files with 614 additions and 4 deletions

View File

@@ -0,0 +1,79 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f8a0dfbcd6854965bb6ae78627009a53, type: 3}
m_Name: ScriptGeneratorSetting
m_EditorClassIdentifier:
_codePath:
_namespace: GameLogic
_widgetName: item
CodeStyle: 0
scriptGenerateRule:
- uiElementRegex: m_go
componentName: GameObject
isUIWidget: 0
- uiElementRegex: m_item
componentName: GameObject
isUIWidget: 0
- uiElementRegex: m_tf
componentName: Transform
isUIWidget: 0
- uiElementRegex: m_rect
componentName: RectTransform
isUIWidget: 0
- uiElementRegex: m_text
componentName: Text
isUIWidget: 0
- uiElementRegex: m_richText
componentName: RichTextItem
isUIWidget: 0
- uiElementRegex: m_btn
componentName: Button
isUIWidget: 0
- uiElementRegex: m_img
componentName: Image
isUIWidget: 0
- uiElementRegex: m_rimg
componentName: RawImage
isUIWidget: 0
- uiElementRegex: m_scrollBar
componentName: Scrollbar
isUIWidget: 0
- uiElementRegex: m_scroll
componentName: ScrollRect
isUIWidget: 0
- uiElementRegex: m_input
componentName: InputField
isUIWidget: 0
- uiElementRegex: m_grid
componentName: GridLayoutGroup
isUIWidget: 0
- uiElementRegex: m_hlay
componentName: HorizontalLayoutGroup
isUIWidget: 0
- uiElementRegex: m_vlay
componentName: VerticalLayoutGroup
isUIWidget: 0
- uiElementRegex: m_slider
componentName: Slider
isUIWidget: 0
- uiElementRegex: m_group
componentName: ToggleGroup
isUIWidget: 0
- uiElementRegex: m_curve
componentName: AnimationCurve
isUIWidget: 0
- uiElementRegex: m_canvasGroup
componentName: CanvasGroup
isUIWidget: 0
- uiElementRegex: m_tmp
componentName: TextMeshProUGUI
isUIWidget: 0

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fe8fecac349dd7a4cafb77822cdba64a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,12 +1,30 @@
using System.Collections;
using UnityEngine;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using TEngine;
namespace GameLogic
{
[Window(UILayer.Bottom, fullScreen: true)]
[Window(UILayer.UI)]
class BattleMainUI : UIWindow
{
#region
private RectTransform _rectContainer;
private GameObject _itemTouch;
private GameObject _goTopInfo;
private GameObject _itemRoleInfo;
private GameObject _itemMonsterInfo;
protected override void ScriptGenerator()
{
_rectContainer = FindChildComponent<RectTransform>("m_rectContainer");
_itemTouch = FindChild("m_rectContainer/m_itemTouch").gameObject;
_goTopInfo = FindChild("m_goTopInfo").gameObject;
_itemRoleInfo = FindChild("m_goTopInfo/m_itemRoleInfo").gameObject;
_itemMonsterInfo = FindChild("m_goTopInfo/m_itemMonsterInfo").gameObject;
}
#endregion
#region
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 12cde5e16fa64eab883975bd63fc5077
timeCreated: 1741968067

View File

@@ -0,0 +1,303 @@
using System.Collections.Generic;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor.UI
{
public class ScriptGenerator
{
private const string Gap = "/";
[MenuItem("GameObject/ScriptGenerator/UIProperty", priority = 41)]
public static void MemberProperty()
{
Generate(false);
}
[MenuItem("GameObject/ScriptGenerator/UIProperty - UniTask", priority = 43)]
public static void MemberPropertyUniTask()
{
Generate(false, true);
}
[MenuItem("GameObject/ScriptGenerator/UIPropertyAndListener", priority = 42)]
public static void MemberPropertyAndListener()
{
Generate(true);
}
[MenuItem("GameObject/ScriptGenerator/UIPropertyAndListener - UniTask", priority = 44)]
public static void MemberPropertyAndListenerUniTask()
{
Generate(true, true);
}
private static void Generate(bool includeListener, bool isUniTask = false)
{
var root = Selection.activeTransform;
if (root != null)
{
StringBuilder strVar = new StringBuilder();
StringBuilder strBind = new StringBuilder();
StringBuilder strOnCreate = new StringBuilder();
StringBuilder strCallback = new StringBuilder();
Ergodic(root, root, ref strVar, ref strBind, ref strOnCreate, ref strCallback, isUniTask);
StringBuilder strFile = new StringBuilder();
if (includeListener)
{
#if ENABLE_TEXTMESHPRO
strFile.Append("using TMPro;\n");
#endif
if (isUniTask)
{
strFile.Append("using Cysharp.Threading.Tasks;\n");
}
strFile.Append("using UnityEngine;\n");
strFile.Append("using UnityEngine.UI;\n");
strFile.Append("using TEngine;\n\n");
strFile.Append($"namespace {ScriptGeneratorSetting.GetUINameSpace()}\n");
strFile.Append("{\n");
strFile.Append("\t[Window(UILayer.UI)]\n");
strFile.Append("\tclass " + root.name + " : UIWindow\n");
strFile.Append("\t{\n");
}
// 脚本工具生成的代码
strFile.Append("\t\t#region 脚本工具生成的代码\n");
strFile.Append(strVar);
strFile.Append("\t\tprotected override void ScriptGenerator()\n");
strFile.Append("\t\t{\n");
strFile.Append(strBind);
strFile.Append(strOnCreate);
strFile.Append("\t\t}\n");
strFile.Append("\t\t#endregion");
if (includeListener)
{
strFile.Append("\n\n");
// #region 事件
strFile.Append("\t\t#region 事件\n");
strFile.Append(strCallback);
strFile.Append("\t\t#endregion\n\n");
strFile.Append("\t}\n");
strFile.Append("}\n");
}
TextEditor te = new TextEditor();
te.text = strFile.ToString();
te.SelectAll();
te.Copy();
}
Debug.Log($"脚本已生成到剪贴板请自行Ctl+V粘贴");
}
public static void Ergodic(Transform root, Transform transform, ref StringBuilder strVar, ref StringBuilder strBind, ref StringBuilder strOnCreate,
ref StringBuilder strCallback, bool isUniTask)
{
for (int i = 0; i < transform.childCount; ++i)
{
Transform child = transform.GetChild(i);
WriteScript(root, child, ref strVar, ref strBind, ref strOnCreate, ref strCallback, isUniTask);
if (child.name.StartsWith("m_item"))
{
continue;
}
Ergodic(root, child, ref strVar, ref strBind, ref strOnCreate, ref strCallback, isUniTask);
}
}
private static string GetRelativePath(Transform child, Transform root)
{
StringBuilder path = new StringBuilder();
path.Append(child.name);
while (child.parent != null && child.parent != root)
{
child = child.parent;
path.Insert(0, Gap);
path.Insert(0, child.name);
}
return path.ToString();
}
public static string GetBtnFuncName(string varName)
{
var codeStyle = ScriptGeneratorSetting.Instance.CodeStyle;
if (codeStyle == UIFieldCodeStyle.MPrefix)
{
return "OnClick" + varName.Replace("m_btn", string.Empty) + "Btn";
}
else
{
return "OnClick" + varName.Replace("_btn", string.Empty) + "Btn";
}
}
public static string GetToggleFuncName(string varName)
{
var codeStyle = ScriptGeneratorSetting.Instance.CodeStyle;
if (codeStyle == UIFieldCodeStyle.MPrefix)
{
return "OnToggle" + varName.Replace("m_toggle", string.Empty) + "Change";
}
else
{
return "OnToggle" + varName.Replace("_toggle", string.Empty) + "Change";
}
}
public static string GetSliderFuncName(string varName)
{
var codeStyle = ScriptGeneratorSetting.Instance.CodeStyle;
if (codeStyle == UIFieldCodeStyle.MPrefix)
{
return "OnSlider" + varName.Replace("m_slider", string.Empty) + "Change";
}
else
{
return "OnSlider" + varName.Replace("_slider", string.Empty) + "Change";
}
}
private static void WriteScript(Transform root, Transform child, ref StringBuilder strVar, ref StringBuilder strBind, ref StringBuilder strOnCreate,
ref StringBuilder strCallback, bool isUniTask)
{
string varName = child.name;
string componentName = string.Empty;
var rule = ScriptGeneratorSetting.GetScriptGenerateRule().Find(t => varName.StartsWith(t.uiElementRegex));
if (rule != null)
{
componentName = rule.componentName;
}
bool isUIWidget = rule is { isUIWidget: true };
if (componentName == string.Empty)
{
return;
}
var codeStyle = ScriptGeneratorSetting.Instance.CodeStyle;
if (codeStyle == UIFieldCodeStyle.UnderscorePrefix)
{
if (varName.StartsWith("_"))
{
}
else if(varName.StartsWith("m_"))
{
varName = varName.Substring(1);
}
else
{
varName = $"_{varName}";
}
}
else if (codeStyle == UIFieldCodeStyle.MPrefix)
{
if (varName.StartsWith("m_"))
{
}
else if (varName.StartsWith("_"))
{
varName = $"m{varName}";
}
else
{
varName = $"m_{varName}";
}
}
string varPath = GetRelativePath(child, root);
if (!string.IsNullOrEmpty(varName))
{
strVar.Append("\t\tprivate " + componentName + " " + varName + ";\n");
switch (componentName)
{
case "Transform":
strBind.Append($"\t\t\t{varName} = FindChild(\"{varPath}\");\n");
break;
case "GameObject":
strBind.Append($"\t\t\t{varName} = FindChild(\"{varPath}\").gameObject;\n");
break;
case "AnimationCurve":
strBind.Append($"\t\t\t{varName} = FindChildComponent<AnimCurveObject>(\"{varPath}\").m_animCurve;\n");
break;
default:
if (isUIWidget)
{
strBind.Append($"\t\t\t{varName} = CreateWidgetByType<{componentName}>(\"{varPath}\");\n");
}
strBind.Append($"\t\t\t{varName} = FindChildComponent<{componentName}>(\"{varPath}\");\n");
break;
}
if (componentName == "Button")
{
string varFuncName = GetBtnFuncName(varName);
if (isUniTask)
{
strOnCreate.Append($"\t\t\t{varName}.onClick.AddListener(UniTask.UnityAction({varFuncName}));\n");
strCallback.Append($"\t\tprivate async UniTaskVoid {varFuncName}()\n");
strCallback.Append("\t\t{\n await UniTask.Yield();\n\t\t}\n");
}
else
{
strOnCreate.Append($"\t\t\t{varName}.onClick.AddListener({varFuncName});\n");
strCallback.Append($"\t\tprivate void {varFuncName}()\n");
strCallback.Append("\t\t{\n\t\t}\n");
}
}
else if (componentName == "Toggle")
{
string varFuncName = GetToggleFuncName(varName);
strOnCreate.Append($"\t\t\t{varName}.onValueChanged.AddListener({varFuncName});\n");
strCallback.Append($"\t\tprivate void {varFuncName}(bool isOn)\n");
strCallback.Append("\t\t{\n\t\t}\n");
}
else if (componentName == "Slider")
{
string varFuncName = GetSliderFuncName(varName);
strOnCreate.Append($"\t\t\t{varName}.onValueChanged.AddListener({varFuncName});\n");
strCallback.Append($"\t\tprivate void {varFuncName}(float value)\n");
strCallback.Append("\t\t{\n\t\t}\n");
}
}
}
public class GeneratorHelper : EditorWindow
{
[MenuItem("GameObject/ScriptGenerator/About", priority = 49)]
public static void About()
{
GeneratorHelper welcomeWindow = (GeneratorHelper)EditorWindow.GetWindow(typeof(GeneratorHelper), false, "About");
}
public void Awake()
{
minSize = new Vector2(400, 600);
}
protected void OnGUI()
{
GUILayout.BeginVertical();
foreach (var item in ScriptGeneratorSetting.GetScriptGenerateRule())
{
GUILayout.Label(item.uiElementRegex + "\t" + item.componentName);
}
GUILayout.EndVertical();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e5b80682b2036f42b69fcb09c53863a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace TEngine.Editor.UI
{
public enum UIFieldCodeStyle
{
/// <summary>
/// Field names start with underscore (e.g., _variable)
/// </summary>
[InspectorName("Field names start with underscore (e.g., _variable)")]
UnderscorePrefix,
/// <summary>
/// Field names start with m_ prefix (e.g., m_variable)
/// </summary>
[InspectorName("Field names start with m_ prefix (e.g., m_variable)")]
MPrefix,
}
[Serializable]
public class ScriptGenerateRuler
{
public string uiElementRegex;
public string componentName;
public bool isUIWidget = false;
public ScriptGenerateRuler(string uiElementRegex, string componentName, bool isUIWidget = false)
{
this.uiElementRegex = uiElementRegex;
this.componentName = componentName;
this.isUIWidget = isUIWidget;
}
}
[CustomPropertyDrawer(typeof(ScriptGenerateRuler))]
public class ScriptGenerateRulerDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
var uiElementRegexRect = new Rect(position.x, position.y, 120, position.height);
var componentNameRect = new Rect(position.x + 125, position.y, 150, position.height);
var isUIWidgetRect = new Rect(position.x + 325, position.y, 150, position.height);
EditorGUI.PropertyField(uiElementRegexRect, property.FindPropertyRelative("uiElementRegex"), GUIContent.none);
EditorGUI.PropertyField(componentNameRect, property.FindPropertyRelative("componentName"), GUIContent.none);
EditorGUI.PropertyField(isUIWidgetRect, property.FindPropertyRelative("isUIWidget"), GUIContent.none);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
[CreateAssetMenu(menuName = "TEngine/ScriptGeneratorSetting", fileName = "ScriptGeneratorSetting")]
public class ScriptGeneratorSetting : ScriptableObject
{
private static ScriptGeneratorSetting _instance;
public static ScriptGeneratorSetting Instance
{
get
{
if (_instance == null)
{
string[] guids = AssetDatabase.FindAssets("t:ScriptGeneratorSetting");
if (guids.Length >= 1)
{
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
_instance = AssetDatabase.LoadAssetAtPath<ScriptGeneratorSetting>(path);
}
}
return _instance;
}
}
// [FolderPath]
// [LabelText("默认组件代码保存路径")]
[SerializeField]
private string _codePath;
// [LabelText("绑定代码命名空间")]
[SerializeField]
private string _namespace = "GameLogic";
// [LabelText("子组件名称(不会往下继续遍历)")]
[SerializeField]
private string _widgetName = "item";
public string CodePath => _codePath;
public string Namespace => _namespace;
public string WidgetName => _widgetName;
public UIFieldCodeStyle CodeStyle = UIFieldCodeStyle.UnderscorePrefix;
[SerializeField]
private List<ScriptGenerateRuler> scriptGenerateRule = new List<ScriptGenerateRuler>()
{
new ScriptGenerateRuler("m_go", "GameObject"),
new ScriptGenerateRuler("m_item", "GameObject"),
new ScriptGenerateRuler("m_tf", "Transform"),
new ScriptGenerateRuler("m_rect", "RectTransform"),
new ScriptGenerateRuler("m_text", "Text"),
new ScriptGenerateRuler("m_richText", "RichTextItem"),
new ScriptGenerateRuler("m_btn", "Button"),
new ScriptGenerateRuler("m_img", "Image"),
new ScriptGenerateRuler("m_rimg", "RawImage"),
new ScriptGenerateRuler("m_scrollBar", "Scrollbar"),
new ScriptGenerateRuler("m_scroll", "ScrollRect"),
new ScriptGenerateRuler("m_input", "InputField"),
new ScriptGenerateRuler("m_grid", "GridLayoutGroup"),
new ScriptGenerateRuler("m_hlay", "HorizontalLayoutGroup"),
new ScriptGenerateRuler("m_vlay", "VerticalLayoutGroup"),
new ScriptGenerateRuler("m_slider", "Slider"),
new ScriptGenerateRuler("m_group", "ToggleGroup"),
new ScriptGenerateRuler("m_curve", "AnimationCurve"),
new ScriptGenerateRuler("m_canvasGroup", "CanvasGroup"),
new ScriptGenerateRuler("m_tmp","TextMeshProUGUI"),
};
public List<ScriptGenerateRuler> ScriptGenerateRule => scriptGenerateRule;
[MenuItem("TEngine/Create ScriptGeneratorSetting")]
private static void CreateAutoBindGlobalSetting()
{
string[] paths = AssetDatabase.FindAssets("t:ScriptGeneratorSetting");
if (paths.Length >= 1)
{
string path = AssetDatabase.GUIDToAssetPath(paths[0]);
EditorUtility.DisplayDialog("警告", $"已存在ScriptGeneratorSetting路径:{path}", "确认");
return;
}
ScriptGeneratorSetting setting = CreateInstance<ScriptGeneratorSetting>();
AssetDatabase.CreateAsset(setting, "Assets/Editor/ScriptGeneratorSetting.asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
public static List<ScriptGenerateRuler> GetScriptGenerateRule()
{
if (Instance == null)
{
return null;
}
return Instance.ScriptGenerateRule;
}
public static string GetUINameSpace()
{
if (Instance == null)
{
return string.Empty;
}
return Instance.Namespace;
}
public static UIFieldCodeStyle GetCodeStyle()
{
if (Instance == null)
{
return UIFieldCodeStyle.UnderscorePrefix;
}
return Instance.CodeStyle;
}
public static string GetCodePath()
{
if (Instance == null)
{
return string.Empty;
}
return Instance.CodePath;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f8a0dfbcd6854965bb6ae78627009a53
timeCreated: 1741968155