【第二章自定义功能菜单_MenuItemAttribute_顶部菜单栏(本章进度1/7)】
第二章内容较多,打算分三个部分,七个小节进行记录,这是第一小节。自定义顶部菜单栏。
第一部分MenuItemAttribute(菜单项特性类),这个特性允许我们可以在自定义Unity顶部菜单、自定义Hierarchy窗口右键功能菜单、自定义Projcet窗口右键功能菜单、自定义组件下拉菜单列表功能菜单。在这部分里面有一个网格优化实践小项目,我会放到最后一节。
自定义Unity顶部菜单
在使用一些插件或者框架的时候,会看到一些unity顶部自定义一些菜单项(类似下图),这就是通过MenItemAttribute实现的。
下面这代码就是利用MenuItem这个特性,他的使用需要using UnityEditor,使用格式是
xxxx是顶部菜单,yyyy是下一级,算上根菜单,最多支持七级(正常三到四级就行了,别再多了)。点击对应的菜单,会执行下面绑定的静态方法,如果例子图,控制台显示出"Function 1 executed"。
- 先介绍最普通的没有绑定快捷键的。
[MenuItem(“xxxx/yyyy”)]
private static void Function1()
{
Debug.Log(“Function 1 executed”);
}
[MenuItem(“xxxx/xxxx”)]下面必须是无返回值的静态方法
下面是一个使用例子
using UnityEngine;
using UnityEditor;public class MenuItemExample
{[MenuItem("测试菜单/funtion1")]private static void Function1(){Debug.Log("Function 1 executed");}}
效果如下,
- 接下来介绍绑定快捷键的,我们使用的其他插件或者插件的时候,经常可以看到对应的菜单项,后面会显示快捷键,如下图的打包工具 后面有个“F5”,就可以使用F5 来使用打包工具。
使用格式如下
这里注意下面“示例 修饰符+快捷键” 示例和修饰+快捷键中间是有一个空格的
[MenuItem("测试菜单/示例 修饰符+快捷键")]
private static void ShiftExample()
{Debug.Log("快捷键触发");}
#书上说支持四种基础修饰符和F1到F12、箭头键、Home、End、PageUp、PageDown,但是我个人测试下来,四种基础修饰符和F1到F12、箭头键这三类是支持,而Home那些不知道是不是快捷键冲突,我这边无法使用。我搜到unity官方也是要求"菜单快捷键应使用字母键与基础修饰符组合,避免占用系统级功能键",所以,我们只介绍四种基础修饰符和字符组合。
修饰符 | 名称 | 对应按键 | 使用说明 |
---|---|---|---|
% | Control/Command | Windows: Ctrl macOS: Cmd | 最常用的跨平台修饰符 |
# | Shift | Shift | 上档键,区分大小写 |
& | Alt/Option | Windows: Alt macOS: Option | 注意可能和系统菜单快捷键冲突 |
_ | 无修饰(功能键) | 无修饰符 | 仅用于功能键(F1-F12),必须直接加在键名前(如 _F1) |
组合形式 | 对应按键 | 示例 | 说明 |
---|---|---|---|
%# | Ctrl/Cmd + Shift | %#s = Ctrl+Shift+S | 顺序无关(#%s 等效) |
%& | Ctrl/Cmd + Alt | %&d = Ctrl+Alt+D | - |
#& | Shift + Alt | #&e = Shift+Alt+E | - |
%#& | Ctrl/Cmd + Shift + Alt | %#&r = 全修饰键+R | 跨平台行为一致 |
形式 | 说明 | 示例 | 等效组合 |
---|---|---|---|
大写字母 | 自动包含 Shift 修饰符 | %G | %#g |
小写字母 | 无 Shift 修饰符 | %g | 纯 Ctrl/Cmd+g |
下面是使用脚本
using UnityEngine;
using UnityEditor;public class MenuItemExample
{[MenuItem("测试菜单/funtion1")]private static void Function1(){Debug.Log("Function 1 executed");}// 1. Shift 修饰符示例 (#)[MenuItem("测试菜单/Shift 示例 #s")]private static void ShiftExample(){Debug.Log("Shift + S 触发");// 其他可用组合: #a, #b, #c... (区分大小写)}// 2. Ctrl/Cmd 修饰符示例 (%) 这里windows用Ctrl, Mac用Cmd[MenuItem("测试菜单/Ctrl 示例 %t")]private static void CtrlExample(){Debug.Log("Ctrl/Cmd + T 触发");}// 3. Alt 修饰符示例 (&)[MenuItem("测试菜单/Alt 示例 &r")]private static void AltExample(){Debug.Log("Alt + R 触发");// 注意: macOS上是Option键}// 4. 组合键示例 (%# = Ctrl+Shift)[MenuItem("测试菜单/组合键示例 %#c")]private static void ComboExample(){Debug.Log("Ctrl+Shift+C 触发");// 其他组合: %& (Ctrl+Alt), #& (Shift+Alt), %#& (全修饰键)}// 5. 功能键 F1 示例 (_)[MenuItem("测试菜单/F1 示例 _F1")]private static void F1Example(){Debug.Log("F1 键触发");// 其他功能键: _F2, _F3..._F12 用法相同}// 6. 大小写敏感示例[MenuItem("测试菜单/大小写示例 %g")] // 小写 gprivate static void LowercaseExample(){Debug.Log("Ctrl+G 触发");}[MenuItem("测试菜单/大小写示例 %G")] // 大写 Gprivate static void UppercaseExample(){Debug.Log("Ctrl+Shift+G 触发 (自动包含Shift)");}[MenuItem("导航/向左移动 _LEFT")]private static void MoveLeft(){Debug.Log("← 左方向键触发");}[MenuItem("导航/End _End")]private static void End(){Debug.Log("End触发");}
}
效果图
总结
- 基础修饰符:4 种(
%
,#
,&
,_
) - 组合修饰符:4 种(
%#
,%&
,#&
,%#&
)(由基础修饰符组合而成) - 隐含修饰符:大小写字母(自动处理 Shift 状态)
- 总计独立类型:4 种基础类型 + 大小写特殊处理
使用须知
- 所有修饰符仅适用于主菜单栏
- 功能键(F1-F12)必须使用
_
前缀且不能与其他修饰符组合 - 不要使用系统功能键(Home/End/方向键等)
- 字母键区分大小写(
%a ≠ %A
)
- 最后一个点,就是这些选项的排序问题,它默认是按照首字符A-Z排序的,在[MenuItem]属性中,可以通过制定菜单项的优先级来控制其在菜单中的排序,可以通过设置 priority 参数来实现。优先级的默认值是 1000。格式是这样的[MenuItem(“xxx/yyy”, ture/false,优先级)] 例子是[MenuItem(“测试菜单/普通功能”, false, 1)],这里的[MenuItem] 的参数:
• 第一个参数是菜单路径,用于定义菜单项的层级和名称。
• 第二个参数是 isValidate,用于控制是否启用验证函数。如果设置为 false,则直接调用方法;如果设置为 true,则需要提供一个同名的 ValidateMenuItem 方法。
• 第三个参数是 priority,用于定义菜单项的排序顺序。优先级越低,菜单项越靠前。
优先级,好理解,一个数字直观显示优先级,关于第二个参数,介绍完优先级之后,再介绍。
这是设置好的代码
using UnityEngine;
using UnityEditor;public class MenuItemExample
{// 1. 功能键 F1 示例 (_)[MenuItem("测试菜单/F1 示例 _F1", false, 1)] // 设置优先级为 1private static void F1Example(){Debug.Log("F1 键触发");}// 2. 无快捷键示例(直接点击菜单项)[MenuItem("测试菜单/Function1", false, 2)] // 设置优先级为 2private static void Function1(){Debug.Log("Function 1 executed");}// 3. Shift 修饰符示例 (#)[MenuItem("测试菜单/Shift 示例 #s", false, 3)] // 设置优先级为 3private static void ShiftExample(){Debug.Log("Shift + S 触发");}// 4. Ctrl/Cmd 修饰符示例 (%)[MenuItem("测试菜单/Ctrl 示例 %t", false, 4)] // 设置优先级为 4private static void CtrlExample(){Debug.Log("Ctrl/Cmd + T 触发");}// 5. Alt 修饰符示例 (&)[MenuItem("测试菜单/Alt 示例 &r", false, 5)] // 设置优先级为 5private static void AltExample(){Debug.Log("Alt + R 触发");}// 6. 组合键示例 (%# = Ctrl+Shift)[MenuItem("测试菜单/组合键示例 %#c", false, 6)] // 设置优先级为 6private static void ComboExample(){Debug.Log("Ctrl+Shift+C 触发");}// 7. 大小写敏感示例[MenuItem("测试菜单/大小写示例 %g", false, 7)] // 小写 gprivate static void LowercaseExample(){Debug.Log("Ctrl+G 触发");}[MenuItem("测试菜单/大小写示例 %G", false, 8)] // 大写 Gprivate static void UppercaseExample(){Debug.Log("Ctrl+Shift+G 触发 (自动包含Shift)");}
}
这是设置好的效果图,如我们代码设置好的优先级排序
现在介绍一下
isValidate 参数用于控制是否启用验证函数。验证函数的作用是动态决定菜单项是否可用(是否被禁用或显示为灰色)。如果 isValidate 设置为 true,Unity 会尝试调用一个同名的 ValidateMenuItem 方法来判断菜单项是否应该被启用。下面举个代码例子
using UnityEngine;
using UnityEditor;public class MenuItemExample
{// 1. 普通菜单项(无验证)[MenuItem("测试菜单/普通功能", false, 1)]private static void NormalFunction(){Debug.Log("普通功能被执行");}// 2. 需要验证的菜单项(isValidate = true),并添加 F2 快捷键[MenuItem("测试菜单/需要验证的功能 _F2", true, 2)]private static void ValidatedFunction(){Debug.Log("需要验证的功能被执行");}// 3. 验证函数(必须与菜单项方法同名)[MenuItem("测试菜单/需要验证的功能 _F2", false, 2)]private static bool ValidateValidatedFunction(){// 返回 true 表示启用菜单项,返回 false 表示禁用菜单项// 这里我们根据某个条件来判断是否启用return SomeCondition();}// 4. 功能键 F1 示例(无验证)[MenuItem("测试菜单/F1 示例 _F1", false, 3)]private static void F1Example(){Debug.Log("F1 键触发");}// 模拟一个条件判断private static bool SomeCondition(){// 假设我们根据某个条件来判断是否启用菜单项// 这里我们简单地返回一个随机值来模拟return Random.value > 0.5f;}
}
普通菜单项(无验证)
- 代码:
[MenuItem("测试菜单/普通功能", false, 1)] private static void NormalFunction() {Debug.Log("普通功能被执行"); }
- 解释:
isValidate
设置为false
,表示直接调用NormalFunction
方法,不进行验证。 - 行为:这个菜单项始终可用,点击后会输出
"普通功能被执行"
。
需要验证的菜单项
- 代码:
[MenuItem("测试菜单/需要验证的功能 _F2", true, 2)] private static void ValidatedFunction() {Debug.Log("需要验证的功能被执行"); }
- 解释:
isValidate
设置为true
,表示需要调用验证函数。同时,通过_F2
添加了快捷键F2
。 - 行为:当
SomeCondition
返回true
时,菜单项可用,点击或按下F2
键后会输出"需要验证的功能被执行"
。如果SomeCondition
返回false
,菜单项被禁用(显示为灰色),无法点击,按下F2
键也不会触发。
验证函数
- 代码:
[MenuItem("测试菜单/需要验证的功能 _F2", false, 2)] private static bool ValidateValidatedFunction() {// 返回 true 表示启用菜单项,返回 false 表示禁用菜单项// 这里我们通过 SomeCondition 方法来模拟一个条件判断return SomeCondition(); }
- 解释:验证函数的名称必须与菜单项方法的名称一致,但返回值类型为
bool
。isValidate
设置为false
,表示这是验证函数。 - 行为:
ValidateValidatedFunction
方法返回true
表示启用菜单项,返回false
表示禁用菜单项。在这个例子中,我们通过SomeCondition
方法来模拟一个条件判断。如果条件满足(随机值大于 0.5),菜单项可用;否则,菜单项被禁用。
功能键 F1 示例(无验证)
- 代码:
[MenuItem("测试菜单/F1 示例 _F1", false, 3)] private static void F1Example() {Debug.Log("F1 键触发"); }
- 解释:
isValidate
设置为false
,表示直接调用F1Example
方法,不进行验证。通过_F1
添加了快捷键F1
。 - 行为:这个菜单项始终可用,按下
F1
键后会输出"F1 键触发"
。
运行效果
- 普通功能:始终可用,点击后会输出
"普通功能被执行"
。 - 需要验证的功能:
- 如果
SomeCondition
返回true
,菜单项可用,点击或按下F2
键后会输出"需要验证的功能被执行"
。 - 如果
SomeCondition
返回false
,菜单项被禁用(显示为灰色),无法点击,按下F2
键也不会触发。
- 如果
- F1 示例:始终可用,按下
F1
键后会输出"F1 键触发"
。
总结
isValidate
参数:- 设置为
true
时,需要提供一个同名的验证函数(返回值为bool
)。 - 验证函数的名称必须与菜单项方法的名称一致,但返回值类型不同。
- 设置为
- 功能键快捷键:
- 功能键(如
F1
、F2
等)在[MenuItem]
中需要使用_
前缀,例如_F1
、_F2
等。 - 功能键不能与其他修饰符(如
%
、#
、&
)组合。
- 功能键(如
- 验证函数的作用:
- 动态控制菜单项的可用性,可以根据条件返回
true
或false
。
- 动态控制菜单项的可用性,可以根据条件返回
以下是根据 Unity 2022.3 中文官方文档整理的 MenuItem
属性完整笔记表格,也有后面要讲的东西,上文没涉及的:
Unity MenuItem 属性完整参考表
项目 | 说明 | 语法示例 | 注意事项 |
---|---|---|---|
基本语法 | |||
[MenuItem] | 创建自定义菜单项 | [MenuItem("Tools/My Tool")] | 必须放在 static 方法上 |
路径格式 | |||
主菜单路径 | 使用 / 分隔多级菜单 | "Window/Submenu/Tool" | 最多支持7级菜单 |
上下文菜单路径 | 使用 CONTEXT/[组件名] 格式 | "CONTEXT/Rigidbody/Reset" | 右键组件时显示 |
快捷键修饰符 | |||
% | Ctrl(Win)/Cmd(Mac) | %s → Ctrl/Cmd+S | 跨平台自动适配 |
# | Shift | #g → Shift+G | 区分大小写 |
& | Alt | &f → Alt+F | 避免与系统快捷键冲突 |
_ | 无修饰键(仅功能键) | _F1 → F1 | 必须直接加在键名前 |
组合键 | |||
双键组合 | 修饰符连续书写 | %#n → Ctrl/Cmd+Shift+N | 顺序无关 (#%n 等效) |
三键组合 | 全修饰键组合 | %#&d → Ctrl+Shift+Alt+D | macOS 需开启"辅助功能"权限 |
特殊参数 | |||
priority | 菜单项排序位置 | [MenuItem("Tools/Save", false, 10)] | 数值越小位置越靠上,差值≥10显示分割线 |
validate | 验证函数(返回bool) | [MenuItem("Tools/Save", true)] | true=验证方法,false=执行方法 |
键位支持 | |||
支持键位 | 字母(A-Z)、数字(0-9)、功能键(F1-F12) | _F5 , %1 | 字母区分大小写 |
最好别用的键位 | 方向键、PageUp/Down、Home/End 等 | - | 需通过 Event 手动处理 |
使用限制 | |||
菜单类型 | 仅限主菜单栏 | - | 不适用于右键菜单/上下文菜单 |
保留快捷键 | Unity 内置快捷键(如 %z =撤销) | - | 冲突时自定义项会被禁用 |
平台差异 | macOS 需开启辅助功能权限 | - | 系统偏好设置→安全性与隐私→辅助功能 |
官方文档核心要点
-
路径命名空间
CONTEXT/
专用于组件上下文菜单GameObject/
会出现在 GameObject 菜单底部
-
优先级规则
优先级范围 菜单位置 0-999 GameObject 菜单 1000-1999 Assets 菜单 ≥2000 自定义菜单区
完整文档参见:UnityEditor.MenuItem