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