当前位置:   article > 正文

【Unity编辑器扩展】UI变量代码自动生成工具(编辑器扩展干货/大幅提高效率)_unity 编辑器工具

unity 编辑器工具

 工具主要功能:通过扩展编辑器菜单,在不写一行代码的情况下,快速方便灵活的配置生成UI变量、一键添加按钮事件, 大幅提高工作效率。

 工具使用演示

模式一:通过SerializedField方式自动生成UI变量代码,并在Inspector面板自动填充UI变量

模式二:通过GetComponent初始化变量 

 

 移除变量:

一键快速添加Button按钮事件(支持批量处理):

 

2022.9.1更新:

新增了变量绑定模式设置:

模式一:先点Generate Script自动生成[SerializeField]变量,然后点击Bind Properties将组件赋值给[SerializeField]变量;  优点是可以在Inspector面板上显示出每一个变量值,缺点是由于某种bug,直接生成完[SerialzedField]变量代码后,即使做了等待脚本编译和资源导入全部完成后再对[SerializeField]变量赋值,依然会出现"Type mismatch"的情况。所以只能把生成代码和绑定变量拆成两个按钮步骤才能正常。

模式二:不用SerializeField,直接生成通过GetComponent从已绑定GameObject上获取组件的代码。优点是步骤简单,面板显示简洁,不需要保存多个序列化对象。缺点是在界面逻辑实例化时会有调用GetComponent的微微微微微小的开销。

1. 在Prefab UI界面中右键点击节点,指定变量为private/protected/public,然后弹出可选择的变量类型菜单。

 2. 添加绑定后,数据在Inspector面板显示,方便修改变量相关类型等;支持添加变量和数组变量;默认变量名为节点名,变量名重复时自动修改变量名,可自行编辑变量名;支持数组添加/移除;拖动数组元素可调整索引;

 3. 选择变量类型。根据绑定节点筛选出交集组件,展示在下拉列表里以便选择变量类型

 4. 在Hierarchy面板直观显示已绑定的组件信息:

 5. 绑定完UI变量后点击生成代码按钮一键生成对应UIForm的partial类绑定脚本;如果当前UIForm逻辑脚本类没有添加partial修饰符,程序会自动提示添加。UI绑定代码独立生成partial脚本是为了避免与UIForm界面逻辑脚本冲突,比如VS中存在为保存的代码,这时直接操作UIForm类会为保存部分丢失的可能。

功能代码实现:

1. 定义SerializeFieldData类用于记录变量数据,在UI逻辑的基类添加_fields用于存放变量数据列表

  1. [Serializable]
  2. public class SerializeFieldData
  3. {
  4. public string VarName; //变量名
  5. public GameObject[] Targets;//关联的GameObject
  6. public string VarType; //变量类型FullName,带有名字空间
  7. public bool Foldout; //列表展开
  8. public string VarPrefix;//变量private/protect/public
  9. public string VarSampleType;//变量类型不带名字空间
  10. public SerializeFieldData(string varName, GameObject[] targets = null)
  11. {
  12. VarName = varName;
  13. Targets = targets ?? new GameObject[1];
  14. Foldout = true;
  15. }
  16. public T GetComponent<T>(int idx) where T : Component
  17. {
  18. return Targets[idx].GetComponent<T>();
  19. }
  20. public T[] GetComponents<T>() where T : Component
  21. {
  22. T[] result = new T[Targets.Length];
  23. for (int i = 0; i < Targets.Length; i++)
  24. {
  25. result[i] = Targets[i].GetComponent<T>();
  26. }
  27. return result;
  28. }
  29. }
  30. public class UIFormBase : UIFormLogic
  31. {
  32. [HideInInspector][SerializeField] SerializeFieldData[] _fields = new SerializeFieldData[0];
  33. ...
  34. }

2. 自定义UIFormEditor类,扩展编辑器:

[CustomEditor(typeof(UIFormBase), true)] 传入true使编辑器扩展对所有继承自UIFormBase的类都能拥有编辑器扩展部分出的功能。

  1. [CustomEditor(typeof(UIFormBase), true)]
  2. public class UIFormBaseEditor : Editor
  3. {
  4. ...
  5. }

通过注册 EditorApplication.hierarchyWindowItemOnGUI = delegate (int id, Rect rect){...},可以自定义Hierarchy面板的UI显示。如,在已经添加到变量的节点后面用绿色文字显示出变量信息

  1. private static GUIStyle normalStyle;
  2. private static GUIStyle selectedStyle;
  3. [InitializeOnLoadMethod]
  4. static void InitEditor()
  5. {
  6. normalStyle = new GUIStyle();
  7. normalStyle.normal.textColor = Color.white;
  8. selectedStyle = new GUIStyle();
  9. selectedStyle.normal.textColor = Color.green;
  10. Selection.selectionChanged = () =>
  11. {
  12. addToFieldToggle = false;
  13. removeToFieldToggle = false;
  14. };
  15. EditorApplication.hierarchyWindowItemOnGUI = delegate (int id, Rect rect)
  16. {
  17. OpenSelectComponentMenuListener(rect);
  18. var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
  19. if (prefabStage == null)
  20. {
  21. return;
  22. }
  23. var uiForm = prefabStage.prefabContentsRoot.GetComponent<UIFormBase>();
  24. if (uiForm == null)
  25. {
  26. return;
  27. }
  28. var curDrawNode = EditorUtility.InstanceIDToObject(id) as GameObject;
  29. if (curDrawNode == null)
  30. {
  31. return;
  32. }
  33. var fields = uiForm.GetFieldsProperties();
  34. SerializeFieldData drawItem = null;
  35. foreach (var item in fields)
  36. {
  37. if (item == null) continue;
  38. if (ArrayUtility.Contains(item.Targets, curDrawNode))
  39. {
  40. drawItem = item;
  41. break;
  42. }
  43. }
  44. if (drawItem != null)
  45. {
  46. rect.x = rect.xMax - Mathf.Min(300, rect.size.x * 0.35f);
  47. GUI.Label(rect, string.Format("{0} {1} {2}", drawItem.VarPrefix, drawItem.VarSampleType, drawItem.VarName), selectedStyle);
  48. }
  49. };
  50. }

3. 自定义右键菜单:

通过[MenuItem("GameObject/Add UIForm Variable/private", false, priority = 0)],设置路径为GameObject/就出现在Hierarchy的右键菜单里。

  1. static int varPrefixIndex = -1;
  2. static bool mShowSelectTypeMenu;//是否显示变量类型选择菜单
  3. [MenuItem("GameObject/Add UIForm Variable/private", false, priority = 0)]
  4. private static void AddPrivateVariable2UIForm()
  5. {
  6. varPrefixIndex = 0;
  7. mShowSelectTypeMenu = true;
  8. }

4. 变量类型选择菜单框:

在EditorApplication.hierarchyWindowItemOnGUI回调中调用了OpenSelectComponentMenuListener(rect)方法,然后获取选中节点的所有组件的交集,显示在菜单中:

  1. private static void OpenSelectComponentMenuListener(Rect rect)
  2. {
  3. if (mShowSelectTypeMenu)
  4. {
  5. int idx = -1;
  6. var strArr = GetPopupContents(GetTargetsFromSelectedNodes(Selection.gameObjects));
  7. var contents = new GUIContent[strArr.Length];
  8. for (int i = 0; i < strArr.Length; i++)
  9. {
  10. contents[i] = new GUIContent(strArr[i]);
  11. }
  12. rect.width = 200;
  13. rect.height = MathF.Max(100, contents.Length * rect.height);
  14. EditorUtility.DisplayCustomMenu(rect, contents, idx, (userData, contents, selected) =>
  15. {
  16. AddToFields(varPrefixArr[varPrefixIndex], contents[selected]);
  17. }, null);
  18. mShowSelectTypeMenu = false;
  19. }
  20. }

5. 根据选择的变量信息创建SerializeFieldData并存入UI基类的_fields变量列表;

通过PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot可以拿到已打开Prefab的根节点。

  1. private static void AddToFields(string varPrefix, string varType)
  2. {
  3. if (addToFieldToggle)
  4. {
  5. return;
  6. }
  7. if (Selection.count <= 0) return;
  8. var uiForm = GetPrefabRootComponent<UIFormBase>();
  9. if (uiForm == null)
  10. {
  11. Debug.LogWarning("UIForm Script is not exist.");
  12. return;
  13. }
  14. var targets = GetTargetsFromSelectedNodes(Selection.gameObjects);
  15. var fieldsProperties = uiForm.GetFieldsProperties();
  16. if (fieldsProperties == null) fieldsProperties = new SerializeFieldData[0];
  17. Undo.RecordObject(uiForm, uiForm.name);
  18. SerializeFieldData field = new SerializeFieldData(GenerateFieldName(fieldsProperties, targets), targets);
  19. field.VarType = varType;
  20. field.VarSampleType = GetSampleType(field.VarType).Name;
  21. field.VarPrefix = varPrefix;
  22. ArrayUtility.Add(ref fieldsProperties, field);
  23. uiForm.ModifyFieldsProperties(fieldsProperties);
  24. EditorUtility.SetDirty(uiForm);
  25. addToFieldToggle = true;
  26. removeToFieldToggle = false;
  27. }

6. Inspector面板扩展:

先了解一些比较常用的编辑器扩展API:

1.垂直布局: EditorGUILayout.BeginVertical();EditorGUILayout.EndVertical(); 需成对存在;

2.水平布局:EditorGUILayout.BeginHorizontal(); EditorGUILayout.EndHorizontal();需成对存在;

3.代码编译中:EditorApplication.isCompiling

4.资源导入中:EditorApplication.isUpdating

5.警示框:EditorGUILayout.HelpBox("Wiatting for compiling or updating...", MessageType.Warning);

 6.UI禁止交互; 

EditorGUI.BeginDisabledGroup(EditorApplication.isCompiling || EditorApplication.isUpdating);

EditorGUI.EndDisabledGroup();

成对存在,可控制被包裹的UI是否可交互。如上,当编译和导入资源过程让UI不可交互;

7.可折叠按钮:

  1. boolValue = EditorGUILayout.BeginFoldoutHeaderGroup(boolValue, “Title”);
  2. if(boolValue){
  3. 可折叠的UI放到这里
  4. }

 8. 下拉菜单

①EditorGUILayout.Popup:intValue为当前选择的菜单项索引

intValue = EditorGUILayout.Popup(intValue, varPrefixArr, GUILayout.MaxWidth(fieldPrefixWidth));

 ②EditorGUILayout.DropdownButton: 下拉按钮,优点是可以在下拉按钮点击后再加载下拉数据并显示,减少不必要性能消耗。点击下拉按钮后再创建GenericMenu(下拉列表):

  1. if (EditorGUILayout.DropdownButton(new GUIContent(varPrefixProperty.stringValue), FocusType.Passive, GUILayout.MaxWidth(fieldPrefixWidth)))
  2. {
  3. GenericMenu popMenu = new GenericMenu();
  4. foreach (var varPrefix in varPrefixArr)
  5. {
  6. popMenu.AddItem(new GUIContent(varPrefix), varPrefix.CompareTo(varPrefixProperty.stringValue) == 0, selectObj =>
  7. {
  8. varPrefixProperty.stringValue = selectObj.ToString();
  9. serializedObject.ApplyModifiedProperties();
  10. }, varPrefix);
  11. }
  12. popMenu.ShowAsContext();
  13. }

9. 可编辑文本框:

stringValue = GUILayout.TextField(stringValue, GUILayout.MinWidth(fieldItemWidth));

10. 按钮:

  1. if (GUILayout.Button("+", GUILayout.Width(smallButtonWidth)))
  2. {
  3. //按钮被点击
  4. }

 11. 对齐:GUILayout.FlexibleSpace();GUILayout.Space(10);

GUILayout.FlexibleSpace(); 动态间距大小;GUILayout.Space(10)固定间距,空出10个像素

让Label右对齐:

  1. EditorGUILayout.BeginHorizontal();
  2. GUILayout.FlexibleSpace();
  3. EditorGUILayout.LabelField("Label");
  4. EditorGUILayout.EndHorizontal();

 让Label左对齐:

  1. EditorGUILayout.BeginHorizontal();
  2. EditorGUILayout.LabelField("Label");
  3. GUILayout.FlexibleSpace();
  4. EditorGUILayout.EndHorizontal();

让Label居中对齐:

  1. EditorGUILayout.BeginHorizontal();
  2. GUILayout.FlexibleSpace();
  3. EditorGUILayout.LabelField("Label");
  4. GUILayout.FlexibleSpace();
  5. EditorGUILayout.EndHorizontal();
  1. public override void OnInspectorGUI()
  2. {
  3. CheckAndInitFields();
  4. serializedObject.Update();
  5. EditorGUILayout.BeginVertical();
  6. bool disableAct = EditorApplication.isCompiling || EditorApplication.isUpdating || EditorApplication.isPlaying;
  7. if (disableAct)
  8. {
  9. EditorGUILayout.HelpBox("Wiatting for compiling or updating...", MessageType.Warning);
  10. }
  11. EditorGUILayout.BeginHorizontal();
  12. EditorGUI.BeginDisabledGroup(disableAct);
  13. if (GUILayout.Button("1. Generate Script")) //生成脚本
  14. {
  15. GenerateUIFormVariables(uiForm, serializedObject);
  16. }
  17. if (useSerializeMode && GUILayout.Button("2. Bind Properties")) //绑定变量
  18. {
  19. SerializeFieldProperties(serializedObject, uiForm.GetFieldsProperties());
  20. }
  21. if (GUILayout.Button("Open Script"))
  22. {
  23. var monoScript = MonoScript.FromMonoBehaviour(uiForm);
  24. string scriptFile = AssetDatabase.GetAssetPath(monoScript);
  25. InternalEditorUtility.OpenFileAtLineExternal(scriptFile, 0);
  26. }
  27. if (GUILayout.Button("Open Bind Script"))
  28. {
  29. var uiFormClassName = uiForm.GetType().Name;
  30. string scriptFile = UtilityBuiltin.ResPath.GetCombinePath(ConstEditor.UISerializeFieldDir, Utility.Text.Format("{0}.Variables.cs", uiFormClassName));
  31. InternalEditorUtility.OpenFileAtLineExternal(scriptFile, 0);
  32. }
  33. if (GUILayout.Button("Clear", GUILayout.MaxWidth(55)))
  34. {
  35. mFields.ClearArray();
  36. }
  37. EditorGUILayout.EndHorizontal();
  38. EditorGUILayout.BeginHorizontal();
  39. EditorGUI.BeginChangeCheck();
  40. useSerializeMode = EditorGUILayout.ToggleLeft("SerializeMode", useSerializeMode, GUILayout.MaxWidth(115));
  41. if (EditorGUI.EndChangeCheck())
  42. {
  43. UnityEditor.EditorPrefs.SetBool("SerializeMode", useSerializeMode);
  44. }
  45. GUILayout.FlexibleSpace();
  46. if (GUILayout.Button(EditorGUIUtility.TrIconContent("_Help", "使用说明"), GUIStyle.none))
  47. {
  48. EditorUtility.DisplayDialog("使用说明", "1.打开UI界面预制体.\n2.右键节点'[Add/Remove] UI Variable'添加/移除变量.\n3.在Inspector面板点击Generate Script生成代码.", "OK");
  49. GUIUtility.ExitGUI();
  50. }
  51. EditorGUILayout.EndHorizontal();
  52. for (int i = 0; i < mFields.arraySize; i++)
  53. {
  54. EditorGUILayout.BeginHorizontal();
  55. GUILayout.Label(i.ToString(), GUILayout.Width(30f));
  56. var item = mFields.GetArrayElementAtIndex(i);
  57. var varNameProperty = item.FindPropertyRelative("VarName");
  58. var varTypeProperty = item.FindPropertyRelative("VarType");
  59. var targetsProperty = item.FindPropertyRelative("Targets");
  60. var unfoldProperty = item.FindPropertyRelative("Foldout");
  61. var varPrefixProperty = item.FindPropertyRelative("VarPrefix");
  62. var varSampleType = item.FindPropertyRelative("VarSampleType");
  63. int targetsCount = targetsProperty != null ? targetsProperty.arraySize : 0;
  64. string targetsTitle = $"Size {targetsCount}";
  65. unfoldProperty.boolValue = EditorGUILayout.BeginFoldoutHeaderGroup(unfoldProperty.boolValue, targetsTitle);
  66. if (EditorGUILayout.DropdownButton(new GUIContent(varPrefixProperty.stringValue), FocusType.Passive, GUILayout.MaxWidth(fieldPrefixWidth)))
  67. {
  68. GenericMenu popMenu = new GenericMenu();
  69. foreach (var varPrefix in varPrefixArr)
  70. {
  71. popMenu.AddItem(new GUIContent(varPrefix), varPrefix.CompareTo(varPrefixProperty.stringValue) == 0, selectObj =>
  72. {
  73. varPrefixProperty.stringValue = selectObj.ToString();
  74. serializedObject.ApplyModifiedProperties();
  75. }, varPrefix);
  76. }
  77. popMenu.ShowAsContext();
  78. }
  79. if (EditorGUILayout.DropdownButton(new GUIContent(varTypeProperty.stringValue), FocusType.Passive, GUILayout.MaxWidth(fieldTypeWidth)))
  80. {
  81. GenericMenu popMenu = new GenericMenu();
  82. var popContens = GetPopupContents(targetsProperty);
  83. foreach (var tpName in popContens)
  84. {
  85. popMenu.AddItem(new GUIContent(tpName), tpName.CompareTo(varTypeProperty.stringValue) == 0, selectObj =>
  86. {
  87. varTypeProperty.stringValue = selectObj.ToString();
  88. varSampleType.stringValue = GetSampleType(varTypeProperty.stringValue).Name;
  89. serializedObject.ApplyModifiedProperties();
  90. }, tpName);
  91. }
  92. popMenu.ShowAsContext();
  93. }
  94. varNameProperty.stringValue = GUILayout.TextField(varNameProperty.stringValue, GUILayout.MinWidth(fieldItemWidth));
  95. if (GUILayout.Button("+", GUILayout.Width(smallButtonWidth)))
  96. {
  97. InsertField(i + 1);
  98. }
  99. if (GUILayout.Button("-", GUILayout.Width(smallButtonWidth)))
  100. {
  101. RemoveField(i);
  102. }
  103. EditorGUILayout.EndHorizontal();
  104. if (i < mFields.arraySize && mFields.GetArrayElementAtIndex(i).FindPropertyRelative("Foldout").boolValue)
  105. {
  106. item = mFields.GetArrayElementAtIndex(i);
  107. targetsProperty = item.FindPropertyRelative("Targets");
  108. mCurFieldIdx = i;
  109. if (mReorderableList[i] == null)
  110. {
  111. mReorderableList[i] = new ReorderableList(serializedObject, targetsProperty, true, false, true, true);
  112. mReorderableList[i].drawElementCallback = DrawTargets;
  113. }
  114. else
  115. {
  116. mReorderableList[i].serializedProperty = targetsProperty;
  117. }
  118. mReorderableList[i].DoLayoutList();
  119. }
  120. EditorGUILayout.EndFoldoutHeaderGroup();
  121. }
  122. if (serializedObject.hasModifiedProperties)
  123. {
  124. serializedObject.ApplyModifiedProperties();
  125. serializedObject.Update();
  126. }
  127. EditorGUI.EndDisabledGroup();
  128. EditorGUILayout.EndVertical();
  129. base.OnInspectorGUI();
  130. }

12. 获取GameObject上挂的脚本文件所在目录:

  1. var monoScript = MonoScript.FromMonoBehaviour(monoBehaviorInstance);
  2. string scriptPath = AssetDatabase.GetAssetPath(monoScript);//.cs脚本文件路径

13. 显示对话框:

EditorUtility.DisplayDialog("标题", "内容", "确定按钮",“取消按钮”);

14. 打开文件夹:EditorUtility.RevealInFinder(path);

选择文件:EditorUtility.OpenFilePanel()

选择文件夹:EditorUtility.OpenFolderPanel()

15. 可调数组顺序/可增删的列表ReorderableList用法:

  1. mReorderableList[i] = new ReorderableList(serializedObject, arrayProperty, true, false, true, true);
  2. mReorderableList[i].drawElementCallback = DrawTargets;
  3. private void DrawTargets(Rect rect, int index, bool isActive, bool isFocused)
  4. {
  5. EditorGUI.BeginDisabledGroup(EditorApplication.isCompiling || EditorApplication.isUpdating || EditorApplication.isPlaying);
  6. var field = mFields.GetArrayElementAtIndex(mCurFieldIdx);
  7. var targetsProperty = field.FindPropertyRelative("Targets");
  8. var targetProperty = targetsProperty.GetArrayElementAtIndex(index);
  9. var curRect = new Rect(rect.x, rect.y, 20, EditorGUIUtility.singleLineHeight);
  10. EditorGUI.LabelField(curRect, index.ToString());
  11. curRect.x += 20;
  12. curRect.width = rect.width - 20;
  13. EditorGUI.ObjectField(curRect, targetProperty, GUIContent.none);
  14. EditorGUI.EndDisabledGroup();
  15. }

16. EditorPrefs,作用同PlayerPrefs,可用于保存编辑器设置等。如:EditorPrefs.SetBool("SerializeMode", useSerializeMode);

17. 通过调用编辑器API在代码里打开脚本文件:

InternalEditorUtility.OpenFileAtLineExternal(scriptFile, 0); 甚至可以指定打开并定位到代码的几行几列。

18. 支持撤销操作Undo.RecordObject(uiForm, uiForm.name); 例如移除UI变量时提前调用Undo.RecordObject记录对象状态,然后用户撤销操作时会自动回退到这一状态:

  1. private void RemoveField(int idx)
  2. {
  3. Undo.RecordObject(uiForm, uiForm.name);//记录移除数组元素前的对象状态
  4. mFields.DeleteArrayElementAtIndex(idx);
  5. ArrayUtility.RemoveAt(ref mReorderableList, idx);
  6. }

 19. 使用Unity编辑器内置图标。Unity编辑器有很多内置图标,并且是可以用获取到共用的。获取编辑器内置图标API: EditorGUIUtility.TrIconContent(), EditorGUIUtility.IconContent(); 只需传入图标名即可。

如显示一个帮助按钮, IconContent和TrIconContent的区别就是,TrIconContent("_Help", "使用说明")当鼠标悬停在图标上会有显示提示; 

  1. if (GUILayout.Button(EditorGUIUtility.TrIconContent("_Help", "使用说明"), GUIStyle.none))
  2. {
  3. EditorUtility.DisplayDialog("使用说明", "1.打开UI界面预制体.\n2.右键节点'[Add/Remove] UI Variable'添加/移除变量.\n3.在Inspector面板点击Generate Script生成代码.", "OK");
  4. GUIUtility.ExitGUI();
  5. }

如何知道Unity所有内置图标名称呢?可以写个编辑器通过Resources.FindObjectsOfTypeAll<Texture2D>()获取全部图标,并显示出每个图标和对应图标名,一目了然。

当然也有人已经整理列出了图标及名称,但是由于Unity版本不同图标样式也会有区别,白嫖地址:GitHub - halak/unity-editor-icons

20. 一键清理prefab丢失脚本Missing Scrips, GameObjectUtility.RemoveMonoBehavioursWithMissingScript(pfb);

  1. [MenuItem("Game Framework/GameTools/Clear Missing Scripts【清除Prefab丢失脚本】")]
  2. public static void ClearMissingScripts()
  3. {
  4. var pfbArr = AssetDatabase.FindAssets("t:Prefab");
  5. foreach (var item in pfbArr)
  6. {
  7. var pfbFileName = AssetDatabase.GUIDToAssetPath(item);
  8. var pfb = AssetDatabase.LoadAssetAtPath<GameObject>(pfbFileName);
  9. GameObjectUtility.RemoveMonoBehavioursWithMissingScript(pfb);
  10. }
  11. }

自动生成UI变量核心代码:

  1. #if UNITY_EDITOR
  2. using UnityEditor.SceneManagement;
  3. using System.Linq;
  4. using UnityEditor;
  5. using System.Collections.Generic;
  6. using UnityEngine;
  7. using UnityEditorInternal;
  8. using System;
  9. using GameFramework;
  10. using System.Text.RegularExpressions;
  11. using System.Text;
  12. using System.IO;
  13. [CustomEditor(typeof(UIFormBase), true)]
  14. public class UIFormBaseEditor : Editor
  15. {
  16. readonly static string[] varPrefixArr = { "private", "protected", "public" };
  17. const string arrFlag = "Arr";
  18. const float fieldItemWidth = 100;
  19. const float fieldPrefixWidth = 100;
  20. const float fieldTypeWidth = 240;
  21. const float smallButtonWidth = 25;
  22. SerializedProperty mFields;
  23. static bool addToFieldToggle;
  24. static bool removeToFieldToggle;
  25. ReorderableList[] mReorderableList;
  26. UIFormBase uiForm;
  27. int mCurFieldIdx;
  28. bool useSerializeMode = false;
  29. static int varPrefixIndex = -1;
  30. static bool mShowSelectTypeMenu;
  31. #region #右键菜单
  32. private static GUIStyle normalStyle;
  33. private static GUIStyle selectedStyle;
  34. [InitializeOnLoadMethod]
  35. static void InitEditor()
  36. {
  37. normalStyle = new GUIStyle();
  38. normalStyle.normal.textColor = Color.white;
  39. selectedStyle = new GUIStyle();
  40. selectedStyle.normal.textColor = Color.green;
  41. Selection.selectionChanged = () =>
  42. {
  43. addToFieldToggle = false;
  44. removeToFieldToggle = false;
  45. };
  46. EditorApplication.hierarchyWindowItemOnGUI = delegate (int id, Rect rect)
  47. {
  48. OpenSelectComponentMenuListener(rect);
  49. var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
  50. if (prefabStage == null)
  51. {
  52. return;
  53. }
  54. var uiForm = prefabStage.prefabContentsRoot.GetComponent<UIFormBase>();
  55. if (uiForm == null)
  56. {
  57. return;
  58. }
  59. var curDrawNode = EditorUtility.InstanceIDToObject(id) as GameObject;
  60. if (curDrawNode == null)
  61. {
  62. return;
  63. }
  64. var fields = uiForm.GetFieldsProperties();
  65. SerializeFieldData drawItem = null;
  66. foreach (var item in fields)
  67. {
  68. if (item == null) continue;
  69. if (ArrayUtility.Contains(item.Targets, curDrawNode))
  70. {
  71. drawItem = item;
  72. break;
  73. }
  74. }
  75. if (drawItem != null)
  76. {
  77. rect.x = rect.xMax - Mathf.Min(300, rect.size.x * 0.35f);
  78. GUI.Label(rect, string.Format("{0} {1} {2}", drawItem.VarPrefix, drawItem.VarSampleType, drawItem.VarName), selectedStyle);
  79. }
  80. };
  81. }
  82. [MenuItem("GameObject/UIForm Fields Tool/Add private", false, priority = 2)]
  83. private static void AddPrivateVariable2UIForm()
  84. {
  85. varPrefixIndex = 0;
  86. mShowSelectTypeMenu = true;
  87. }
  88. [MenuItem("GameObject/UIForm Fields Tool/Add protected", false, priority = 3)]
  89. private static void AddProtectedVariable2UIForm()
  90. {
  91. varPrefixIndex = 1;
  92. mShowSelectTypeMenu = true;
  93. }
  94. [MenuItem("GameObject/UIForm Fields Tool/Add public", false, priority = 4)]
  95. private static void AddPublicVariable2UIForm()
  96. {
  97. varPrefixIndex = 2;
  98. mShowSelectTypeMenu = true;
  99. }
  100. [MenuItem("GameObject/UIForm Fields Tool/Remove", false, priority = 5)]
  101. private static void RemoveUIFormVariable()
  102. {
  103. if (removeToFieldToggle)
  104. {
  105. return;
  106. }
  107. if (Selection.count <= 0) return;
  108. var uiForm = GetPrefabRootComponent<UIFormBase>();
  109. if (uiForm == null)
  110. {
  111. Debug.LogWarning("UIForm Script is not exist.");
  112. return;
  113. }
  114. var fieldsProperties = uiForm.GetFieldsProperties();
  115. if (fieldsProperties == null) return;
  116. Undo.RecordObject(uiForm, uiForm.name);
  117. for (int i = 0; i < Selection.gameObjects.Length; i++)
  118. {
  119. var itm = Selection.gameObjects[i];
  120. if (itm == null) continue;
  121. for (int j = fieldsProperties.Length - 1; j >= 0; j--)
  122. {
  123. var fields = fieldsProperties[j];
  124. if (fields == null || fields.Targets == null || fields.Targets.Length <= 0) continue;
  125. for (int k = fields.Targets.Length - 1; k >= 0; k--)
  126. {
  127. if (fields.Targets[k] == itm)
  128. {
  129. if (fields.Targets.Length <= 1)
  130. {
  131. ArrayUtility.RemoveAt(ref fieldsProperties, j);
  132. }
  133. else
  134. {
  135. ArrayUtility.RemoveAt(ref fields.Targets, k);
  136. }
  137. }
  138. }
  139. }
  140. }
  141. uiForm.ModifyFieldsProperties(fieldsProperties);
  142. EditorUtility.SetDirty(uiForm);
  143. removeToFieldToggle = true;
  144. addToFieldToggle = false;
  145. }
  146. /// <summary>
  147. /// 不带名字空间的类型名
  148. /// </summary>
  149. /// <returns></returns>
  150. private static Type GetSampleType(string fullName)
  151. {
  152. Type result = null;
  153. var assemblyArr = AppDomain.CurrentDomain.GetAssemblies();
  154. foreach (var item in assemblyArr)
  155. {
  156. foreach (var tp in item.GetTypes())
  157. {
  158. if (tp.FullName.CompareTo(fullName) == 0)
  159. {
  160. result = tp;
  161. break;
  162. }
  163. }
  164. if (result != null) break;
  165. }
  166. return result;
  167. }
  168. private static T GetPrefabRootComponent<T>() where T : Component
  169. {
  170. var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
  171. if (prefabStage == null)
  172. {
  173. Debug.LogWarning("GetCurrentPrefabStage is null.");
  174. return null;
  175. }
  176. return prefabStage.prefabContentsRoot.GetComponent<T>();
  177. }
  178. private static void AddToFields(string varPrefix, string varType)
  179. {
  180. if (addToFieldToggle)
  181. {
  182. return;
  183. }
  184. if (Selection.count <= 0) return;
  185. var uiForm = GetPrefabRootComponent<UIFormBase>();
  186. if (uiForm == null)
  187. {
  188. Debug.LogWarning("UIForm Script is not exist.");
  189. return;
  190. }
  191. var targets = GetTargetsFromSelectedNodes(Selection.gameObjects);
  192. var fieldsProperties = uiForm.GetFieldsProperties();
  193. if (fieldsProperties == null) fieldsProperties = new SerializeFieldData[0];
  194. Undo.RecordObject(uiForm, uiForm.name);
  195. SerializeFieldData field = new SerializeFieldData(GenerateFieldName(fieldsProperties, targets), targets);
  196. field.VarType = varType;
  197. field.VarSampleType = GetSampleType(field.VarType).Name;
  198. field.VarPrefix = varPrefix;
  199. ArrayUtility.Add(ref fieldsProperties, field);
  200. uiForm.ModifyFieldsProperties(fieldsProperties);
  201. EditorUtility.SetDirty(uiForm);
  202. addToFieldToggle = true;
  203. removeToFieldToggle = false;
  204. }
  205. private static GameObject[] GetTargetsFromSelectedNodes(GameObject[] selectedList)
  206. {
  207. GameObject[] targets = new GameObject[selectedList.Length];
  208. for (int i = 0; i < selectedList.Length; i++)
  209. {
  210. targets[i] = selectedList[i];
  211. }
  212. targets = targets.OrderBy(go => go.transform.GetSiblingIndex()).ToArray();
  213. return targets;
  214. }
  215. #endregion
  216. private void OnEnable()
  217. {
  218. useSerializeMode = EditorPrefs.GetBool("SerializeMode", true);
  219. varPrefixIndex = 0;
  220. mShowSelectTypeMenu = false;
  221. uiForm = (target as UIFormBase);
  222. if (uiForm.GetFieldsProperties() == null)
  223. {
  224. uiForm.ModifyFieldsProperties(new SerializeFieldData[0]);
  225. }
  226. mFields = serializedObject.FindProperty("_fields");
  227. mReorderableList = new ReorderableList[mFields.arraySize];
  228. }
  229. private static void OpenSelectComponentMenuListener(Rect rect)
  230. {
  231. if (mShowSelectTypeMenu)
  232. {
  233. int idx = -1;
  234. var strArr = GetPopupContents(GetTargetsFromSelectedNodes(Selection.gameObjects));
  235. var contents = new GUIContent[strArr.Length];
  236. for (int i = 0; i < strArr.Length; i++)
  237. {
  238. contents[i] = new GUIContent(strArr[i]);
  239. }
  240. rect.width = 200;
  241. rect.height = MathF.Max(100, contents.Length * rect.height);
  242. EditorUtility.DisplayCustomMenu(rect, contents, idx, (userData, contents, selected) =>
  243. {
  244. AddToFields(varPrefixArr[varPrefixIndex], contents[selected]);
  245. }, null);
  246. mShowSelectTypeMenu = false;
  247. }
  248. }
  249. public override void OnInspectorGUI()
  250. {
  251. CheckAndInitFields();
  252. serializedObject.Update();
  253. EditorGUILayout.BeginVertical();
  254. bool disableAct = EditorApplication.isCompiling || EditorApplication.isUpdating || EditorApplication.isPlaying;
  255. if (disableAct)
  256. {
  257. EditorGUILayout.HelpBox("Wiatting for compiling or updating...", MessageType.Warning);
  258. }
  259. EditorGUILayout.BeginHorizontal();
  260. EditorGUI.BeginDisabledGroup(disableAct);
  261. if (GUILayout.Button("1. Generate Script")) //生成脚本
  262. {
  263. GenerateUIFormVariables(uiForm, serializedObject);
  264. }
  265. if (useSerializeMode && GUILayout.Button("2. Bind Properties")) //绑定变量
  266. {
  267. SerializeFieldProperties(serializedObject, uiForm.GetFieldsProperties());
  268. }
  269. if (GUILayout.Button("Open Script"))
  270. {
  271. var monoScript = MonoScript.FromMonoBehaviour(uiForm);
  272. string scriptFile = AssetDatabase.GetAssetPath(monoScript);
  273. InternalEditorUtility.OpenFileAtLineExternal(scriptFile, 0);
  274. }
  275. if (GUILayout.Button("Open Bind Script"))
  276. {
  277. var uiFormClassName = uiForm.GetType().Name;
  278. string scriptFile = UtilityBuiltin.ResPath.GetCombinePath(ConstEditor.UISerializeFieldDir, Utility.Text.Format("{0}.Variables.cs", uiFormClassName));
  279. InternalEditorUtility.OpenFileAtLineExternal(scriptFile, 0);
  280. }
  281. if (GUILayout.Button("Clear", GUILayout.MaxWidth(55)))
  282. {
  283. mFields.ClearArray();
  284. }
  285. EditorGUILayout.EndHorizontal();
  286. EditorGUILayout.BeginHorizontal();
  287. EditorGUI.BeginChangeCheck();
  288. useSerializeMode = EditorGUILayout.ToggleLeft("SerializeMode", useSerializeMode, GUILayout.MaxWidth(115));
  289. if (EditorGUI.EndChangeCheck())
  290. {
  291. UnityEditor.EditorPrefs.SetBool("SerializeMode", useSerializeMode);
  292. }
  293. GUILayout.FlexibleSpace();
  294. if (GUILayout.Button(EditorGUIUtility.TrIconContent("_Help", "使用说明"), GUIStyle.none))
  295. {
  296. EditorUtility.DisplayDialog("使用说明", "1.打开UI界面预制体.\n2.右键节点'[Add/Remove] UI Variable'添加/移除变量.\n3.在Inspector面板点击Generate Script生成代码.", "OK");
  297. GUIUtility.ExitGUI();
  298. }
  299. EditorGUILayout.EndHorizontal();
  300. for (int i = 0; i < mFields.arraySize; i++)
  301. {
  302. EditorGUILayout.BeginHorizontal();
  303. GUILayout.Label(i.ToString(), GUILayout.Width(30f));
  304. var item = mFields.GetArrayElementAtIndex(i);
  305. var varNameProperty = item.FindPropertyRelative("VarName");
  306. var varTypeProperty = item.FindPropertyRelative("VarType");
  307. var targetsProperty = item.FindPropertyRelative("Targets");
  308. var unfoldProperty = item.FindPropertyRelative("Foldout");
  309. var varPrefixProperty = item.FindPropertyRelative("VarPrefix");
  310. var varSampleType = item.FindPropertyRelative("VarSampleType");
  311. int targetsCount = targetsProperty != null ? targetsProperty.arraySize : 0;
  312. string targetsTitle = $"Size {targetsCount}";
  313. unfoldProperty.boolValue = EditorGUILayout.BeginFoldoutHeaderGroup(unfoldProperty.boolValue, targetsTitle);
  314. if (EditorGUILayout.DropdownButton(new GUIContent(varPrefixProperty.stringValue), FocusType.Passive, GUILayout.MaxWidth(fieldPrefixWidth)))
  315. {
  316. GenericMenu popMenu = new GenericMenu();
  317. foreach (var varPrefix in varPrefixArr)
  318. {
  319. popMenu.AddItem(new GUIContent(varPrefix), varPrefix.CompareTo(varPrefixProperty.stringValue) == 0, selectObj =>
  320. {
  321. varPrefixProperty.stringValue = selectObj.ToString();
  322. serializedObject.ApplyModifiedProperties();
  323. }, varPrefix);
  324. }
  325. popMenu.ShowAsContext();
  326. }
  327. if (EditorGUILayout.DropdownButton(new GUIContent(varTypeProperty.stringValue), FocusType.Passive, GUILayout.MaxWidth(fieldTypeWidth)))
  328. {
  329. GenericMenu popMenu = new GenericMenu();
  330. var popContens = GetPopupContents(targetsProperty);
  331. foreach (var tpName in popContens)
  332. {
  333. popMenu.AddItem(new GUIContent(tpName), tpName.CompareTo(varTypeProperty.stringValue) == 0, selectObj =>
  334. {
  335. varTypeProperty.stringValue = selectObj.ToString();
  336. varSampleType.stringValue = GetSampleType(varTypeProperty.stringValue).Name;
  337. serializedObject.ApplyModifiedProperties();
  338. }, tpName);
  339. }
  340. popMenu.ShowAsContext();
  341. }
  342. varNameProperty.stringValue = GUILayout.TextField(varNameProperty.stringValue, GUILayout.MinWidth(fieldItemWidth));
  343. if (GUILayout.Button("+", GUILayout.Width(smallButtonWidth)))
  344. {
  345. InsertField(i + 1);
  346. }
  347. if (GUILayout.Button("-", GUILayout.Width(smallButtonWidth)))
  348. {
  349. RemoveField(i);
  350. }
  351. EditorGUILayout.EndHorizontal();
  352. if (i < mFields.arraySize && mFields.GetArrayElementAtIndex(i).FindPropertyRelative("Foldout").boolValue)
  353. {
  354. item = mFields.GetArrayElementAtIndex(i);
  355. targetsProperty = item.FindPropertyRelative("Targets");
  356. mCurFieldIdx = i;
  357. if (mReorderableList[i] == null)
  358. {
  359. mReorderableList[i] = new ReorderableList(serializedObject, targetsProperty, true, false, true, true);
  360. mReorderableList[i].drawElementCallback = DrawTargets;
  361. }
  362. else
  363. {
  364. mReorderableList[i].serializedProperty = targetsProperty;
  365. }
  366. mReorderableList[i].DoLayoutList();
  367. }
  368. EditorGUILayout.EndFoldoutHeaderGroup();
  369. }
  370. if (serializedObject.hasModifiedProperties)
  371. {
  372. serializedObject.ApplyModifiedProperties();
  373. serializedObject.Update();
  374. }
  375. EditorGUI.EndDisabledGroup();
  376. EditorGUILayout.EndVertical();
  377. base.OnInspectorGUI();
  378. }
  379. /// <summary>
  380. /// 生成UI脚本.cs
  381. /// </summary>
  382. /// <param name="uiForm"></param>
  383. /// <param name="uiFormSerializer"></param>
  384. private void GenerateUIFormVariables(UIFormBase uiForm, SerializedObject uiFormSerializer)
  385. {
  386. if (uiForm == null) return;
  387. var monoScript = MonoScript.FromMonoBehaviour(uiForm);
  388. var uiFormClassName = monoScript.GetClass().Name;
  389. string scriptFile = UtilityBuiltin.ResPath.GetCombinePath(ConstEditor.UISerializeFieldDir, Utility.Text.Format("{0}.Variables.cs", uiFormClassName));
  390. var fields = uiForm.GetFieldsProperties();
  391. if (fields == null || fields.Length <= 0)
  392. {
  393. if (File.Exists(scriptFile))
  394. {
  395. File.Delete(scriptFile);
  396. }
  397. var metaFile = scriptFile + ".meta";
  398. if (File.Exists(metaFile))
  399. {
  400. File.Delete(metaFile);
  401. }
  402. AssetDatabase.Refresh();
  403. return;
  404. }
  405. var matchResult = Regex.Match(monoScript.text, Utility.Text.Format("partial[\\s]+class[\\s]+{0}", uiFormClassName));
  406. string scriptPath = AssetDatabase.GetAssetPath(monoScript);
  407. if (!matchResult.Success)
  408. {
  409. EditorUtility.DisplayDialog("生成UI变量失败!", Utility.Text.Format("请先手动为{0}类添加'partial'修饰符!\n{1}", uiFormClassName, scriptPath), "OK");
  410. return;
  411. }
  412. List<string> nameSpaceList = new List<string> { "UnityEngine" };//默认自带的名字空间
  413. List<string> fieldList = new List<string>();
  414. foreach (var field in fields)
  415. {
  416. if (string.IsNullOrWhiteSpace(field.VarType) || string.IsNullOrWhiteSpace(field.VarName))
  417. {
  418. continue;
  419. }
  420. var varType = GetSampleType(field.VarType);
  421. if (varType == null)
  422. {
  423. continue;
  424. }
  425. if (!string.IsNullOrEmpty(varType.Namespace) && !nameSpaceList.Contains(varType.Namespace))
  426. {
  427. nameSpaceList.Add(varType.Namespace);
  428. }
  429. bool isArray = field.Targets.Length > 1;
  430. var varPrefix = field.VarPrefix;// varPrefixArr[field.VarPrefixSelectedIdx];
  431. string serializeFieldPrefix = useSerializeMode ? "[SerializeField] " : "";
  432. string fieldLine;
  433. if (isArray)
  434. {
  435. fieldLine = Utility.Text.Format("{0}{1} {2}[] {3} = null;", serializeFieldPrefix, varPrefix, varType.Name, field.VarName);
  436. }
  437. else
  438. {
  439. fieldLine = Utility.Text.Format("{0}{1} {2} {3} = null;", serializeFieldPrefix, varPrefix, varType.Name, field.VarName);
  440. }
  441. fieldList.Add(fieldLine);
  442. }
  443. StringBuilder stringBuilder = new StringBuilder();
  444. stringBuilder.AppendLine("//---------------------------------");
  445. stringBuilder.AppendLine("//此文件由工具自动生成,请勿手动修改");
  446. stringBuilder.AppendLine($"//更新自:{CloudProjectSettings.userName}");
  447. stringBuilder.AppendLine($"//更新时间:{DateTime.Now}");
  448. stringBuilder.AppendLine("//---------------------------------");
  449. foreach (var item in nameSpaceList)
  450. {
  451. stringBuilder.AppendLine(Utility.Text.Format("using {0};", item));
  452. }
  453. string uiFormClassNameSpace = monoScript.GetClass().Namespace;
  454. bool hasNameSpace = !string.IsNullOrWhiteSpace(uiFormClassNameSpace);
  455. if (hasNameSpace)
  456. {
  457. stringBuilder.AppendLine(Utility.Text.Format("namespace {0}", uiFormClassNameSpace));
  458. stringBuilder.AppendLine("{");
  459. }
  460. stringBuilder.AppendLine(Utility.Text.Format("public partial class {0}", uiFormClassName));
  461. stringBuilder.AppendLine("{");
  462. if (useSerializeMode)
  463. {
  464. stringBuilder.AppendLine("\t[Space(10)]");
  465. stringBuilder.AppendLine("\t[Header(\"Auto Genertate Properties:\")]");
  466. }
  467. foreach (var item in fieldList)
  468. {
  469. stringBuilder.AppendLine("\t" + item);
  470. }
  471. //不使用Serialize模式时直接生成获取组件的代码
  472. if (!useSerializeMode)
  473. {
  474. GeneratePropertiesUseGetComponent(stringBuilder, fields);
  475. }
  476. stringBuilder.AppendLine("}");
  477. if (hasNameSpace) stringBuilder.AppendLine("}");
  478. File.WriteAllText(scriptFile, stringBuilder.ToString());
  479. AssetDatabase.Refresh();
  480. //SerializeFieldProperties(uiFormSerializer, fields);
  481. }
  482. private void GeneratePropertiesUseGetComponent(StringBuilder stringBuilder, SerializeFieldData[] fields)
  483. {
  484. stringBuilder.AppendLine("\tprotected override void InitUIProperties()");
  485. stringBuilder.AppendLine("\t{");
  486. stringBuilder.AppendLine("\t\tvar fields = this.GetFieldsProperties();");
  487. for (int i = 0; i < fields.Length; i++)
  488. {
  489. var field = fields[i];
  490. bool isArray = field.Targets.Length > 1;
  491. bool isGameObject = field.VarType.CompareTo(typeof(GameObject).FullName) == 0;
  492. if (isArray)
  493. {
  494. if (isGameObject)
  495. stringBuilder.AppendLine(Utility.Text.Format("\t\t{0} = fields[{1}].Targets;", field.VarName, i));
  496. else
  497. stringBuilder.AppendLine(Utility.Text.Format("\t\t{0} = fields[{1}].GetComponents<{2}>();", field.VarName, i, field.VarSampleType));
  498. }
  499. else
  500. {
  501. if (isGameObject)
  502. stringBuilder.AppendLine(Utility.Text.Format("\t\t{0} = fields[{1}].Targets[0];", field.VarName, i));
  503. else
  504. stringBuilder.AppendLine(Utility.Text.Format("\t\t{0} = fields[{1}].GetComponent<{2}>(0);", field.VarName, i, field.VarSampleType));
  505. }
  506. }
  507. stringBuilder.AppendLine("\t}");
  508. }
  509. private void SerializeFieldProperties(SerializedObject serializedObject, SerializeFieldData[] fields)
  510. {
  511. if (serializedObject == null)
  512. {
  513. Debug.LogError("生成UI SerializedField失败, serializedObject为null");
  514. return;
  515. }
  516. foreach (var item in fields)
  517. {
  518. string varName = item.VarName;
  519. string varType = item.VarType;
  520. bool isGameObject = varType.CompareTo(typeof(GameObject).FullName) == 0;
  521. var property = serializedObject.FindProperty(varName);
  522. if (property == null) continue;
  523. if (item.Targets.Length <= 1)
  524. {
  525. property.objectReferenceValue = isGameObject ? item.Targets[0] : item.Targets[0]?.GetComponent(GetSampleType(varType));
  526. }
  527. else if (property.isArray)
  528. {
  529. property.ClearArray();
  530. for (int i = 0; i < item.Targets.Length; i++)
  531. {
  532. if (i >= property.arraySize)
  533. {
  534. property.InsertArrayElementAtIndex(i);
  535. }
  536. property.GetArrayElementAtIndex(i).objectReferenceValue = isGameObject ? item.Targets[i] : item.Targets[i]?.GetComponent(GetSampleType(varType));
  537. }
  538. //for (int i = property.arraySize - 1; i >= item.Targets.Length; i--)
  539. //{
  540. // property.DeleteArrayElementAtIndex(i);
  541. //}
  542. }
  543. }
  544. serializedObject.ApplyModifiedProperties();
  545. }
  546. private void CheckAndInitFields()
  547. {
  548. if (uiForm.GetFieldsProperties() == null)
  549. {
  550. uiForm.ModifyFieldsProperties(new SerializeFieldData[0]);
  551. }
  552. if (mFields == null) mFields = serializedObject.FindProperty("_fields");
  553. if (mFields.arraySize != mReorderableList.Length)
  554. {
  555. if (mFields.arraySize > mReorderableList.Length)
  556. {
  557. for (int i = mReorderableList.Length; i < mFields.arraySize; i++)
  558. {
  559. ArrayUtility.Insert(ref mReorderableList, mReorderableList.Length, null);
  560. }
  561. }
  562. else
  563. {
  564. for (int i = mFields.arraySize; i < mReorderableList.Length; i++)
  565. {
  566. ArrayUtility.RemoveAt(ref mReorderableList, mReorderableList.Length - 1);
  567. }
  568. }
  569. }
  570. }
  571. private void InsertField(int idx)
  572. {
  573. Undo.RecordObject(uiForm, uiForm.name);
  574. mFields.InsertArrayElementAtIndex(idx);
  575. ArrayUtility.Insert(ref mReorderableList, idx, null);
  576. var lastField = mFields.GetArrayElementAtIndex(idx);
  577. if (lastField != null)
  578. {
  579. var lastVarName = lastField.FindPropertyRelative("VarName");
  580. if (!string.IsNullOrEmpty(lastVarName.stringValue))
  581. {
  582. lastVarName.stringValue += idx.ToString();
  583. }
  584. }
  585. }
  586. private void RemoveField(int idx)
  587. {
  588. Undo.RecordObject(uiForm, uiForm.name);
  589. mFields.DeleteArrayElementAtIndex(idx);
  590. ArrayUtility.RemoveAt(ref mReorderableList, idx);
  591. }
  592. private void DrawTargets(Rect rect, int index, bool isActive, bool isFocused)
  593. {
  594. EditorGUI.BeginDisabledGroup(EditorApplication.isCompiling || EditorApplication.isUpdating || EditorApplication.isPlaying);
  595. var field = mFields.GetArrayElementAtIndex(mCurFieldIdx);
  596. var targetsProperty = field.FindPropertyRelative("Targets");
  597. var targetProperty = targetsProperty.GetArrayElementAtIndex(index);
  598. var curRect = new Rect(rect.x, rect.y, 20, EditorGUIUtility.singleLineHeight);
  599. EditorGUI.LabelField(curRect, index.ToString());
  600. curRect.x += 20;
  601. curRect.width = rect.width - 20;
  602. EditorGUI.ObjectField(curRect, targetProperty, GUIContent.none);
  603. EditorGUI.EndDisabledGroup();
  604. }
  605. private static string[] GetPopupContents(GameObject[] targets)
  606. {
  607. if (targets == null || targets.Length <= 0)
  608. {
  609. return new string[0];
  610. }
  611. var typeNames = GetIntersectionComponents(targets);
  612. if (typeNames == null || typeNames.Length <= 0)
  613. {
  614. return new string[0];
  615. }
  616. ArrayUtility.Insert(ref typeNames, 0, typeof(GameObject).FullName);
  617. return typeNames;
  618. }
  619. private static string[] GetPopupContents(SerializedProperty targets)
  620. {
  621. var goArr = new GameObject[targets.arraySize];
  622. for (int i = 0; i < targets.arraySize; i++)
  623. {
  624. var pp = targets.GetArrayElementAtIndex(i);
  625. goArr[i] = (pp != null && pp.objectReferenceValue != null) ? (pp.objectReferenceValue as GameObject) : null;
  626. }
  627. return GetPopupContents(goArr);
  628. }
  629. private static string[] GetIntersectionComponents(GameObject[] targets)
  630. {
  631. var firstItm = targets[0];
  632. if (firstItm == null)
  633. {
  634. return new string[0];
  635. }
  636. var coms = firstItm.GetComponents(typeof(Component));
  637. coms = coms.Distinct().ToArray();//去重
  638. for (int i = coms.Length - 1; i >= 1; i--)
  639. {
  640. var comType = coms[i].GetType().FullName;
  641. bool allContains = true;
  642. for (int j = 1; j < targets.Length; j++)
  643. {
  644. var target = targets[j];
  645. if (target == null) return new string[0];
  646. var tComs = target.GetComponents(typeof(Component));
  647. bool containsType = false;
  648. for (int k = 0; k < tComs.Length; k++)
  649. {
  650. if (tComs[k].GetType().FullName.CompareTo(comType) == 0)
  651. {
  652. containsType = true;
  653. break;
  654. }
  655. }
  656. allContains &= containsType;
  657. if (!allContains) break;
  658. }
  659. if (!allContains)
  660. {
  661. ArrayUtility.RemoveAt(ref coms, i);
  662. }
  663. }
  664. string[] typesArr = new string[coms.Length];
  665. for (int i = 0; i < coms.Length; i++)
  666. {
  667. typesArr[i] = coms[i].GetType().FullName;
  668. }
  669. return typesArr;
  670. }
  671. /// <summary>
  672. /// 生成一个与变量列表里不重名的变量名
  673. /// </summary>
  674. /// <param name="fields"></param>
  675. /// <param name="name"></param>
  676. /// <returns></returns>
  677. private static string GenerateFieldName(SerializeFieldData[] fields, GameObject[] targets)
  678. {
  679. var go = targets[0];
  680. string varName = Regex.Replace(go.name, "[^\\w]", string.Empty);
  681. if (fields == null || fields.Length <= 0)
  682. {
  683. return FirstCharToLower(targets.Length > 1 ? varName + arrFlag : varName);
  684. }
  685. bool contains = false;
  686. foreach (SerializeFieldData item in fields)
  687. {
  688. if (item != null && item.VarName.CompareTo(varName) == 0)
  689. {
  690. contains = true;
  691. }
  692. }
  693. if (targets.Length > 1)
  694. {
  695. varName += arrFlag;
  696. }
  697. if (contains)
  698. {
  699. varName += go.GetInstanceID();
  700. }
  701. return FirstCharToLower(varName);
  702. }
  703. private static string FirstCharToLower(string str)
  704. {
  705. if (string.IsNullOrEmpty(str))
  706. {
  707. return string.Empty;
  708. }
  709. return str.Substring(0, 1).ToLower() + str.Substring(1);
  710. }
  711. }
  712. #endif

工具源码:GitHub - sunsvip/GF_HybridCLR

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/115287
推荐阅读
相关标签
  

闽ICP备14008679号