【Unity编辑器】使用反射和Attribute实现自定义右键菜单

版权声明:本文为博主原创文章,未经博主允许不得转载 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");
    }
}

效果如下:


在自定义窗口任何位置右键就可以出来了。

猜你喜欢

转载自blog.csdn.net/mobilebbki399/article/details/51869480