using System.Collections.Generic; using UnityEngine; using UnityEditor; using UnityEngine.UI; namespace Zion.Scripts.Editor { /// /// 树状菜单生成器编辑器工具 /// public class TreeMenuGenerator : EditorWindow { private TreeMenuData menuData; private SerializedObject serializedData; private SerializedProperty menuItemsProperty; private Vector2 scrollPosition; private GameObject menuRoot; // 预制体路径 private const string ButtonPrefabPath = "UI/Button"; private const string TextPrefabPath = "UI/Text"; // 编辑器窗口尺寸 private const float WindowWidth = 600f; private const float WindowHeight = 700f; [MenuItem("Zion/树状菜单生成器")] public static void ShowWindow() { var window = GetWindow("树状菜单生成器"); window.minSize = new Vector2(WindowWidth, WindowHeight); window.Show(); } private void OnEnable() { // 加载编辑器图标 folderIcon = EditorGUIUtility.FindTexture("Folder Icon"); fileIcon = EditorGUIUtility.FindTexture("TextAsset Icon"); } private void OnGUI() { EditorGUILayout.BeginVertical(GUI.skin.box); // 标题 EditorGUILayout.LabelField("树状菜单生成器", EditorStyles.boldLabel); EditorGUILayout.Space(); // 菜单数据对象 EditorGUI.BeginChangeCheck(); menuData = (TreeMenuData)EditorGUILayout.ObjectField("菜单数据", menuData, typeof(TreeMenuData), false); if (EditorGUI.EndChangeCheck()) { if (menuData != null) { serializedData = new SerializedObject(menuData); menuItemsProperty = serializedData.FindProperty("menuItems"); } else { serializedData = null; menuItemsProperty = null; } } EditorGUILayout.Space(); // 如果没有菜单数据,显示创建按钮 if (menuData == null) { EditorGUILayout.HelpBox("请先创建或选择一个树状菜单数据对象", MessageType.Info); if (GUILayout.Button("创建菜单数据", GUILayout.Height(30))) { CreateNewMenuData(); } EditorGUILayout.EndVertical(); return; } // 更新序列化对象 serializedData.Update(); // 菜单基本设置 DrawMenuBasicSettings(); EditorGUILayout.Space(); // 显示添加根菜单项按钮 if (GUILayout.Button("添加根菜单项", GUILayout.Height(25))) { Undo.RecordObject(menuData, "添加根菜单项"); menuData.menuItems.Add(new TreeMenuItem()); EditorUtility.SetDirty(menuData); } EditorGUILayout.Space(); // 使用滚动视图显示菜单结构 scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.ExpandHeight(true)); // 绘制所有根菜单项 for (int i = 0; i < menuItemsProperty.arraySize; i++) { DrawMenuItem(menuItemsProperty.GetArrayElementAtIndex(i), 0, i); } EditorGUILayout.EndScrollView(); // 应用修改 serializedData.ApplyModifiedProperties(); EditorGUILayout.Space(); // 菜单生成区域 EditorGUILayout.BeginHorizontal(); menuRoot = (GameObject)EditorGUILayout.ObjectField("菜单根对象", menuRoot, typeof(GameObject), true); if (GUILayout.Button("生成菜单", GUILayout.Width(100), GUILayout.Height(25))) { if (menuRoot != null) { GenerateMenu(); } else { EditorUtility.DisplayDialog("错误", "请先选择一个菜单根对象", "确定"); } } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } // 绘制菜单基本设置 private void DrawMenuBasicSettings() { EditorGUILayout.LabelField("菜单基本设置", EditorStyles.boldLabel); EditorGUI.indentLevel++; menuData.menuTitle = EditorGUILayout.TextField("菜单标题", menuData.menuTitle); menuData.backgroundColor = EditorGUILayout.ColorField("背景颜色", menuData.backgroundColor); menuData.textColor = EditorGUILayout.ColorField("文本颜色", menuData.textColor); menuData.highlightColor = EditorGUILayout.ColorField("高亮颜色", menuData.highlightColor); menuData.menuFont = (Font)EditorGUILayout.ObjectField("菜单字体", menuData.menuFont, typeof(Font), false); menuData.fontSize = EditorGUILayout.IntField("字体大小", menuData.fontSize); menuData.itemHeight = EditorGUILayout.FloatField("菜单项高度", menuData.itemHeight); menuData.indentWidth = EditorGUILayout.FloatField("缩进宽度", menuData.indentWidth); EditorGUI.indentLevel--; } // 用于显示的图标 private Texture folderIcon; private Texture fileIcon; // 绘制单个菜单项 private void DrawMenuItem(SerializedProperty menuItemProperty, int level, int index) { SerializedProperty nameProperty = menuItemProperty.FindPropertyRelative("name"); SerializedProperty iconProperty = menuItemProperty.FindPropertyRelative("icon"); SerializedProperty actionProperty = menuItemProperty.FindPropertyRelative("action"); SerializedProperty childrenProperty = menuItemProperty.FindPropertyRelative("children"); SerializedProperty expandedProperty = menuItemProperty.FindPropertyRelative("isExpanded"); // 缩进 EditorGUILayout.BeginHorizontal(); GUILayout.Space(level * 20); // 折叠箭头 expandedProperty.boolValue = EditorGUILayout.Foldout(expandedProperty.boolValue, "", true); // 菜单项图标 Texture icon = childrenProperty.arraySize > 0 ? folderIcon : fileIcon; GUILayout.Label(icon, GUILayout.Width(20), GUILayout.Height(20)); // 菜单项名称 nameProperty.stringValue = EditorGUILayout.TextField(nameProperty.stringValue, GUILayout.ExpandWidth(true)); // 操作按钮 if (GUILayout.Button("+", GUILayout.Width(25))) { // 添加子菜单 AddChildMenuItem(childrenProperty); } if (GUILayout.Button("×", GUILayout.Width(25))) { // 删除菜单项 DeleteMenuItem(menuItemProperty, level, index); EditorGUILayout.EndHorizontal(); return; } EditorGUILayout.EndHorizontal(); // 如果展开,显示更多属性和子项 if (expandedProperty.boolValue) { EditorGUILayout.BeginHorizontal(); GUILayout.Space((level + 1) * 20); EditorGUILayout.LabelField("图标:", GUILayout.Width(40)); iconProperty.stringValue = EditorGUILayout.TextField(iconProperty.stringValue); EditorGUILayout.EndHorizontal(); EditorGUILayout.BeginHorizontal(); GUILayout.Space((level + 1) * 20); EditorGUILayout.LabelField("动作:", GUILayout.Width(40)); actionProperty.stringValue = EditorGUILayout.TextField(actionProperty.stringValue); EditorGUILayout.EndHorizontal(); // 绘制子菜单项 for (int i = 0; i < childrenProperty.arraySize; i++) { DrawMenuItem(childrenProperty.GetArrayElementAtIndex(i), level + 1, i); } } } // 添加子菜单项 private void AddChildMenuItem(SerializedProperty childrenProperty) { childrenProperty.arraySize++; SerializedProperty newItem = childrenProperty.GetArrayElementAtIndex(childrenProperty.arraySize - 1); newItem.FindPropertyRelative("name").stringValue = "新菜单项"; newItem.FindPropertyRelative("children").arraySize = 0; newItem.FindPropertyRelative("isExpanded").boolValue = true; } // 删除菜单项 private void DeleteMenuItem(SerializedProperty menuItemProperty, int level, int index) { SerializedProperty parentProperty; if (level == 0) { parentProperty = menuItemsProperty; } else { // 需要获取父级属性 parentProperty = GetParentProperty(menuItemProperty); } if (parentProperty != null) { // 删除前记录更改 Undo.RecordObject(menuData, "删除菜单项"); // 删除项 parentProperty.DeleteArrayElementAtIndex(index); EditorUtility.SetDirty(menuData); } } // 获取父级属性(此方法为简化版,真实项目中可能需要更复杂的实现) private SerializedProperty GetParentProperty(SerializedProperty property) { string path = property.propertyPath; int lastDotIndex = path.LastIndexOf('.'); if (lastDotIndex == -1) return null; string parentPath = path.Substring(0, lastDotIndex); return serializedData.FindProperty(parentPath); } // 创建新的菜单数据对象 private void CreateNewMenuData() { // 创建保存路径 string path = EditorUtility.SaveFilePanelInProject("创建菜单数据", "TreeMenuData", "asset", "请选择保存位置"); if (string.IsNullOrEmpty(path)) return; // 创建菜单数据对象 TreeMenuData newMenuData = CreateInstance(); newMenuData.menuItems = new List(); // 保存资源 AssetDatabase.CreateAsset(newMenuData, path); AssetDatabase.SaveAssets(); // 选择并显示新建的资源 menuData = newMenuData; serializedData = new SerializedObject(menuData); menuItemsProperty = serializedData.FindProperty("menuItems"); EditorGUIUtility.PingObject(newMenuData); } // 生成菜单UI private void GenerateMenu() { if (menuData == null || menuRoot == null) return; // 记录Undo Undo.RegisterFullObjectHierarchyUndo(menuRoot, "生成菜单"); // 清除现有子对象 while (menuRoot.transform.childCount > 0) { DestroyImmediate(menuRoot.transform.GetChild(0).gameObject); } // 创建标题 GameObject titleObj = new GameObject("Title"); titleObj.transform.SetParent(menuRoot.transform, false); Text titleText = titleObj.AddComponent(); titleText.text = menuData.menuTitle; titleText.font = menuData.menuFont != null ? menuData.menuFont : Resources.GetBuiltinResource("Arial.ttf"); titleText.fontSize = menuData.fontSize + 4; titleText.color = menuData.textColor; titleText.alignment = TextAnchor.MiddleCenter; RectTransform titleRect = titleObj.GetComponent(); titleRect.anchorMin = new Vector2(0, 1); titleRect.anchorMax = new Vector2(1, 1); titleRect.pivot = new Vector2(0.5f, 1); titleRect.sizeDelta = new Vector2(0, 40); titleRect.anchoredPosition = Vector2.zero; // 创建滚动视图 GameObject scrollViewObj = new GameObject("ScrollView"); scrollViewObj.transform.SetParent(menuRoot.transform, false); ScrollRect scrollRect = scrollViewObj.AddComponent(); Image scrollImage = scrollViewObj.AddComponent(); scrollImage.color = new Color(1, 1, 1, 0.1f); RectTransform scrollRectTransform = scrollViewObj.GetComponent(); scrollRectTransform.anchorMin = new Vector2(0, 0); scrollRectTransform.anchorMax = new Vector2(1, 1); scrollRectTransform.pivot = new Vector2(0.5f, 0.5f); scrollRectTransform.sizeDelta = new Vector2(0, -40); scrollRectTransform.anchoredPosition = new Vector2(0, -20); // 创建视口 GameObject viewportObj = new GameObject("Viewport"); viewportObj.transform.SetParent(scrollViewObj.transform, false); Image viewportImage = viewportObj.AddComponent(); viewportImage.color = Color.clear; Mask viewportMask = viewportObj.AddComponent(); viewportMask.showMaskGraphic = false; RectTransform viewportRect = viewportObj.GetComponent(); viewportRect.anchorMin = Vector2.zero; viewportRect.anchorMax = Vector2.one; viewportRect.pivot = new Vector2(0.5f, 0.5f); viewportRect.sizeDelta = Vector2.zero; viewportRect.anchoredPosition = Vector2.zero; // 创建内容容器 GameObject contentObj = new GameObject("Content"); contentObj.transform.SetParent(viewportObj.transform, false); VerticalLayoutGroup layoutGroup = contentObj.AddComponent(); layoutGroup.padding = new RectOffset(5, 5, 5, 5); layoutGroup.spacing = 2; layoutGroup.childAlignment = TextAnchor.UpperLeft; layoutGroup.childControlHeight = false; // 不自动控制高度 layoutGroup.childControlWidth = true; layoutGroup.childForceExpandHeight = false; layoutGroup.childForceExpandWidth = true; ContentSizeFitter sizeFitter = contentObj.AddComponent(); sizeFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained; sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; RectTransform contentRect = contentObj.GetComponent(); contentRect.anchorMin = new Vector2(0, 1); contentRect.anchorMax = new Vector2(1, 1); contentRect.pivot = new Vector2(0.5f, 1); contentRect.anchoredPosition = Vector2.zero; // 设置滚动视图引用 scrollRect.viewport = viewportRect; scrollRect.content = contentRect; scrollRect.horizontal = false; scrollRect.vertical = true; // 创建菜单项 foreach (var item in menuData.menuItems) { GenerateMenuItem(item, contentObj.transform, 0); } // 确保UI更新 Canvas.ForceUpdateCanvases(); LayoutRebuilder.ForceRebuildLayoutImmediate(contentRect); Debug.Log("菜单生成完成!"); } // 生成单个菜单项 private void GenerateMenuItem(TreeMenuItem item, Transform parent, int level) { // 创建菜单项对象 GameObject menuItemObj = new GameObject(item.name); menuItemObj.transform.SetParent(parent, false); // 添加按钮组件 Button button = menuItemObj.AddComponent