版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/mobilebbki399/article/details/51869480
unity提供了许多Attribute,比如[MenuItem]和[ContextMenu],一个是在编辑器菜单栏中添加菜单按钮,一个是在检视面板添加上下文菜单,由于其原理是基于C#的Attribute功能,我们自然可以想到,是否可以自己编写一套自定义Attribute,来实现一个自定义的菜单呢,效果如下:
实现的方式主要用了反射和C#的Attribute,(Attribute实际上还可以做更多功能,这里只是举一个例子)
首先自定义一个Attribute类,内容很简单,这里我在我的Attribute里实现了两个属性,一个是菜单上显示的内容,一个是优先级:
using System;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class InspectorContextMenuItemAttribute : Attribute
{
public string menuItem;
public int priority;
public InspectorContextMenuItemAttribute(string menuItem, int priority)
{
this.menuItem = menuItem;
this.priority = priority;
}
public InspectorContextMenuItemAttribute(string menuItem) : this(menuItem, 0)
{
}
}
然后是子菜单,子菜单通过GenericMenu来实现:
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Reflection;
public class InspectorContextMenu
{
public GenericMenu menu;
private List<AttributeData> attributeList;
public InspectorContextMenu()
{
LoadCustomContextMenuItem();
CreateContextMenuItem();
}
public void ShowMenu()
{
menu.ShowAsContext();
}
void LoadCustomContextMenuItem()
{
if (attributeList != null)
return;
attributeList = new List<AttributeData>();
Assembly assembly = GetType().Assembly;
if (assembly != null)
{
System.Type[] types = assembly.GetTypes();
for (int i = 0; i < types.Length; i++)
{
if (!types[i].IsClass)
continue;
//获取静态方法
MethodInfo[] methods =
types[i].GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
for (int j = 0; j < methods.Length; j++)
{
var attributes = methods[j].GetCustomAttributes(typeof (InspectorContextMenuItemAttribute), false);
foreach (var attribute in attributes)
{
attributeList.Add(new AttributeData(methods[j], (InspectorContextMenuItemAttribute) attribute));
}
}
}
}
//通过优先级排序
attributeList.Sort(Comparison);
}
void CreateContextMenuItem()
{
if (menu != null)
return;
if (attributeList == null)
return;
menu = new GenericMenu();
int level = 1;
for (int i = 0; i < attributeList.Count; i++)
{
//每1000优先级为一组,不同组之间用横线隔开
if (i > 0 && attributeList[i].attribute.priority - level * 1000 >= 0)
{
string line = "";
int id = attributeList[i].attribute.menuItem.LastIndexOf('/');
if (id >= 0)
line = attributeList[i].attribute.menuItem.Substring(0, id);
menu.AddSeparator(line + "/");
level += 1;
}
menu.AddItem(new GUIContent(attributeList[i].attribute.menuItem), false, attributeList[i].Invoke);
}
}
int Comparison(AttributeData A, AttributeData B)
{
return A.attribute.priority - B.attribute.priority;
}
class AttributeData
{
public MethodInfo method;
public InspectorContextMenuItemAttribute attribute;
public AttributeData(MethodInfo method, InspectorContextMenuItemAttribute attribute)
{
this.method = method;
this.attribute = attribute;
}
public void Invoke()
{
if (method != null)
method.Invoke(null, null);
}
}
}
之后就可以在自定义的Inspector或是EditorWindow中使用了,这里写了个EditorWindow中使用的例子:
using UnityEngine;
using UnityEditor;
using System.Collections;
public class TestWindow : EditorWindow
{
private InspectorContextMenu menu
{
get
{
if (m_Menu == null)
m_Menu = new InspectorContextMenu();
return m_Menu;
}
}
private InspectorContextMenu m_Menu;
[MenuItem("Test/TestWindow")]
static void Init()
{
TestWindow window = EditorWindow.GetWindow<TestWindow>();
}
void OnGUI()
{
if (Event.current.type == EventType.MouseDown && Event.current.button == 1)
{
menu.ShowMenu();
Event.current.Use();
}
}
[InspectorContextMenuItem("TestButtonA")]
static void TestButtonA()
{
Debug.Log("ClickTestButtonA");
}
[InspectorContextMenuItem("TestButtonB", 1000)]
static void TestButtonB()
{
Debug.Log("ClickTestButtonB");
}
[InspectorContextMenuItem("TestButtonC", 1001)]
static void TestButtonC()
{
Debug.Log("ClickTestButtonC");
}
[InspectorContextMenuItem("TestButtonD")]
static void TestButtonD()
{
Debug.Log("ClickTestButtonD");
}
}
效果如下:
在自定义窗口任何位置右键就可以出来了。