Unity编辑器扩展----Menu Items

发表于2015-10-21
评论0 3.5k浏览

Unity编辑器扩展----Menu Items

Unity编辑器允许添加自定义Menus,这些添加的自定义Menus可以像内置的Menus一样工作。这个特性对于某些需要频繁使用的功能非常有用,可以将这些常用功能放到编辑器的UI(Menu)上来提高使用效率。本节将展示如何在Unity编辑器上创建Menu Items,并对每种使用方法都给出相应的示例。

添加Menu Items

为了在顶层toolbar上添加一个menu,需要创建一个脚本文件(该文件放在工程的Editor目录下)。在该脚本文件中,编写一个静态方法,并将该静态方法标记为MenuItem属性。
比如,一个常见的应用场景是创建一个名为“Tools”的Menu来提供一些团队常用的功能。
以下为添加一个“Tools”菜单的代码。

using UnityEngine;using UnityEditor;public class MenuItems{[MenuItem("Tools/Clear PlayerPrefs")]private static void NewMenuOption(){PlayerPrefs.DeleteAll();}}

以上代码创建了一个名为“Tools”的Menu,并在Tools菜单下添加了一个"Clear PlayerPrefs"子菜单项。效果如下图所示。

也可以在一个已经存在的Menu下新建Menu Items(如在Window菜单下新建),同样的,为了更好地组织菜单结构,可以按需创建多层次的菜单和菜单项。

using UnityEngine;using UnityEditor;public class MenuItemsExample{// Add a new menu item under an existing menu[MenuItem("Window/New Option")]private static void NewMenuOption(){}// Add a menu item with multiple levels of nesting[MenuItem("Tools/SubMenu/Option")]private static void NewNestedOption(){}}

以上代码的效果如下所示。

热键

Unity编辑器也允许自定义快捷键来提示工作效率。可以给新建的Menu Items指定“热键和快捷键”组合来自动加载内容。
以下为支持的键:

  • %-Windows上Ctrl/OSX上CMD
  • #-Shift
  • &-Alt
  • LEFT/RIGHT/UP/DOWN-Arrow keys
  • F1...F2-F keys
  • HOME,END,PGUP,PGDN

对于字符型的keys,由于不是一个key序列,需要在他们前面加上下划线前缀(如快捷键“G”对应于_g)。
热键的字符放在menu item路径的后面,前面使用空格隔离,如下代码所示。

// Add a new menu item with hotkey CTRL-SHIFT-A [MenuItem("Tools/New Option %#a")] private static void NewMenuOption(){}// Add a new menu item with hotkey CTRL-G[MenuItem("Tools/Item %g")]private static void NewNestedOption(){}// Add a new menu item with hotkey G[MenuItem("Tools/Item2 _g")]private static void NewOptionWithHotkey(){}

带有热键的menu items,显示的时候也会显示热键的名称。例如,上面的代码可以达到如下图片的效果。

注意:Unity对于重复的热键并没有检验机制!如果对多个menu items定义了一个热键,最终在使用热键时,将只有一个热键可用。

特殊路径

如上所示,传递给MenuItem属性的控制着新创建的item被放在哪个顶层menu的下面。
Unity有几个特殊的路径,用于创建上下文菜单(通过右键弹出的菜单项)。

  • Assets-菜单项在“Assets”菜单下可见,在project view下右键也可以得到。
  • Assets/Create-菜单项被放在project view的“Create”按钮下(当增加可以被添加到project的新类型时,很有用)。
  • CONTEXT/ComponentName-在一个给定组件的inspector上右键可以得到的菜单项。

以下是一些如何使用这些特殊路径的例子。

// Add a new menu item that is accessed by right-clicking on an asset in the project view[MenuItem("Assets/Load Additive Scene")]private static void LoadAdditiveScene(){var selected = Selection.activeObject;EditorApplication.OpenSceneAdditive(AssetDatabase.GetAssetPath(selected));}// Adding a new menu item under Assets/Create[MenuItem("Assets/Create/Add Configuration")]private static void AddConfig(){// Create and add a new ScriptableObject for storing configuration}// Add a new menu item that is accessed by right-clicking inside the RigidBody component[MenuItem("CONTEXT/Rigidbody/New Option")]private static void NewOpenForRigidBody(){}

这段代码的执行结果是增加了如下所示的几个菜单 
Assets(project视图)上的右键菜单

在Assets的“Create”按钮下新建的“New option”菜单 
为Rigidbody组件新建的“New Option”上下文菜单

验证

有些menu items只能在指定的上下文下才能显示,而在其他情况下应该是“变灰”状态。根据上下文信息“enabling/disabling” menu items是通过验证方法完成的。
验证方法是一种静态方法,也是使用MenuItem属性标记得到,需要给方法的参数传递一个true值。
验证方法需要和需要被验证的菜单项有相同的路径,同时,需要返回一个bool值来决定当前菜单项是否显示。
例如,验证方法可以用于在project视图下给Texture增加一个右键菜单。

[MenuItem("Assets/ProcessTexture")]private static void DoSomethingWithTexture(){}// Note that we pass the same path, and also pass "true" to the second argument.[MenuItem("Assets/ProcessTexture", true)]private static bool NewMenuOptionValidation(){// This returns true when the selected object is a Texture2D (the menu item will be disabled otherwise).return Selection.activeObject.GetType() == typeof(Texture2D);}

当在project视图上右键任意非texture对象时,菜单项都是灰色的。如下图所示。

通过优先级控制顺序

优先级是一个可以指定给菜单项的数字(传递给MenuItem属性),用于控制在根menu下的这些菜单项的显示顺序。
菜单项也根据指定的优先级自动的分组。

[MenuItem("NewMenu/Option1", false, 1)]private static void NewMenuOption(){}[MenuItem("NewMenu/Option2", false, 2)]private static void NewMenuOption2(){}[MenuItem("NewMenu/Option3", false, 3)]private static void NewMenuOption3(){}[MenuItem("NewMenu/Option4", false, 51)]private static void NewMenuOption4(){}[MenuItem("NewMenu/Option5", false, 52)]private static void NewMenuOption5(){}

以上代码会根据指定的优先级生成两组代码项,如下图所示:

如果想在现有的Unity菜单下新增和组织菜单项,因为大多数内置的菜单项也是使用的优先级,所以会需要一点儿想象力。另一种实现思路是使用类似于Reflector的工具查看Unity的源码,找到创建编辑器菜单的地方。

相关类

以下是一些在新增菜单项时可能会被用到的类。

MenuCommand

当向Inspector上增加新的菜单项(使用“CONTEXT/Component”)时,有时会需要得到当前右键的对象(如用于修改数据)。
通过MenuCommand给静态方法传递参数可以实现这个功能。

[MenuItem("CONTEXT/RigidBody/New Option")]private static void NewMenuOption(MenuCommand menuCommand){// The RigidBody component can be extracted from the menu command using the context field.var rigid = menuCommand.context as RigidBody;}

如上代码所示,当调用菜单项时,当前点击的组件能够通过context得到。

ContextMenu

该属性定义了上下文菜单项。该属性和使用MenuItem属性定义一个方法并传递“CONTEXT/...”效果一样。
不同之处在于,通过该方法你可以为一个给定的组件定义一个默认的上下文菜单,而使用MenuItem时,却是继承自其它组件的菜单。
例子-为组件提供一个上下文菜单来清空其数据。

public class NameBehaviour : MonoBehaviour{public string Name;[ContextMenu("Reset Name")]private static void ResetName(){Name = string.Empty;}}

ContextMenuItem

这个属性可以被用于component(MonoBehaviour)类的域上,用于增加上下文菜单。当ContextMenu属性被添加到组件级别来添加上下文菜单时,标记为ContextMenuItem属性的域将会被增加一个右键菜单。
鉴于该属性是被添加到一个域而不是方法上的,该属性接收两个参数:菜单项的显示名称、菜单被选中时会执行的方法名称。
例子--添加一个方法用于随即初始化一个组件的域。

public class NameBehaviour : MonoBehaviour{[ContextMenuItem("Randomize Name", "Randomize")]public string Name;private void Randomize(){Name = "Some Random Name";}}

这段代码的执行效果如下所示:

结论

从上文所述可以看出,扩展Unity编辑器实现用于自定义菜单项还是非常直观的。
开发通用功能并显示在编辑器上,让所有的开发人员一起使用,还是能够节省大量开发时间的。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引