Unity编辑器制作多级下拉菜单

Unity编辑器下拉菜单

  大家好,我是阿赵。
  在Unity引擎里面编写工具插件,有时候会用到一些特殊的菜单形式,比如下拉选项。
通过下拉菜单,给用户选择不同的选项。
  如果只是一层的下拉列表,可以用EditorGUILayout.Popup来实现
在这里插入图片描述

  简单的代码示例如下:

    string[] tempList = new string[] { "菜单1", "菜单2", "菜单3" };private int tempIndex = 0;private void ShowPopMenu3(){tempIndex = EditorGUILayout.Popup(tempIndex, tempList, GUILayout.Width(100));
}

  但如果是多级的下拉菜单,像这样:
在这里插入图片描述

  EditorGUILayout.Popup是做不到的。
  这里介绍2种方式在Unity的EditorWindow里面制作多层下拉选项。

一、 通过MenuItem实现

1、 MenuItem的介绍

  在using UnityEditor;之后,就可以在代码里面给某个方法添加MenuItem标签,比如这样:

[MenuItem("Tools/测试下拉菜单")]
static void TestFun()
{}

  这时候,在编辑器的菜单栏上面,就会出现菜单选项
在这里插入图片描述

  注意,上面MenuItem里面只设置了一个参数,就是菜单的路径。这个路径是有特殊含义的,现在是”Tools/测试下拉菜单”,那么出现在菜单栏里面的将会是“Tools”入口,然后再下拉就会出现“测试下拉菜单”的选项。
  如果再复杂一点,把路径改成

    [MenuItem("Tools/测试下拉菜单/下拉菜单选项1")]static void TestFun1(){}[MenuItem("Tools/测试下拉菜单/下拉菜单选项2")]static void TestFun2(){PopMenuTestWin.Instance.Show();}

  这时候,显示会变成:
在这里插入图片描述

  所以我们可以知道,如果路径相同的情况下,MenuItem选项会合并显示。
  然后路径的根目录的不同,也会出现不同的效果,比如改成

    [MenuItem("Assets/测试下拉菜单/下拉菜单选项1")]static void TestFun1(){}[MenuItem("Assets/测试下拉菜单/下拉菜单选项2")]static void TestFun2(){}

  把根目录改成“Assets”,那么菜单会出现在Project栏里面,鼠标右键点击某个文件夹,就会出现这组菜单。
在这里插入图片描述

  如果把根目录改成GameObject,并且把最后的属性值设置1-49

[MenuItem("GameObject/测试下拉菜单",false,1)]
static void TestFun1()
{}

在这里插入图片描述

  这时候MenuItem的菜单会显示在Hierarchy面板的鼠标右键菜单里面。

2、 多级MenuItem显示

  假如我现在需要显示的是多层的下拉菜单,通过MenuItem的自动合并路径的特性,就可以这样:

[MenuItem("testMenu/测试菜单1/测试菜单1-1",false,101)]
static void TestMenu1_1()
{}
[MenuItem("testMenu/测试菜单1/测试菜单1-2",false,102)]
static void TestMenu1_2()
{}
[MenuItem("testMenu/测试菜单1/测试菜单1-3",false,103)]
static void TestMenu1_3()
{}
[MenuItem("testMenu/测试菜单2/测试菜单2-1",false,201)]static void TestMenu2_1()
{}
[MenuItem("testMenu/测试菜单2/测试菜单2-2",false,202)]static void TestMenu2_2()
{}
[MenuItem("testMenu/测试菜单3/测试菜单3-1",false,301)]static void TestMenu3_1()
{}
[MenuItem("testMenu/测试菜单4", false, 401)]static void TestMenu4_1()
{}

  当这样定义了之后,那么在菜单上面就可以看到:
在这里插入图片描述

  想继续添加子菜单,只需要继续扩展路径就行了。在MenuItem的最后有一个数字的参数,其实是一个排序显示的参数。数字越大,同级菜单的排序就会在越下面。

3、 把MenuItem显示到EditorWindow里面

  刚才说了很久MenuItem的用法,接下来要把MenuItem显示在EditorWindow里面。
  代码很简单,在需要显示的地方,加上:

if (GUI.Button(new Rect(0,0,100,30), "test"))
{EditorUtility.DisplayPopupMenu(new Rect(0, 30, 0, 0), "testMenu/", null);
}

  这段代码的意思很简单,在按下按钮的时候,调用EditorUtility.DisplayPopupMenu,在指定的区域Rect(0, 30, 0, 0)内,显示菜单路径为”testMenu”作为根目录的所有菜单。
  所以现在的效果会变成这样:
在这里插入图片描述

4、 优缺点

1.优点

  这个方式制作编辑器下拉菜单,最大的好处是简单,添加得也很随意。当想添加或者删除菜单的时候,基本上可以做到互不影响。

2. 缺点

  这个方式的缺点也很明显。
  首先,这种方式制作的菜单,除了会显示在想要的编辑器面板里面,还会显示在顶部菜单栏或者其他的右键菜单里面,很难看。
  然后,由于注册的代码是需要先写死的,所以很难通过配表之类的方式动态修改菜单选项。

二、 通过GenericMenu实现

1、GenericMenu的基本使用方法

  GenericMenu的实现,需要先创建一个GenericMenu对象,然后给这个Menu对象添加Item。核心的代码大概就是这样:

GenericMenu menu = new GenericMenu();
menu.AddItem(new GUIContent(“测试菜单1/测试菜单1-1”), bool, OnSelectMenu,obj);

主要看看AddItem的三个参数:
第一个是路径,也就是显示菜单的层级
第二个是一个布尔值,如果想显示打钩,可以穿true。一般要配合别的选择逻辑去实现
第三OnSelectMenu是选项被触发时的回调参数,比如这样:

private void OnSelectMenu(object val)
{
}

第四个参数是一个object,对应前面的回调函数的参数,当选项被触发之后,会把指定的值传给回调函数。
最后,调用一下

menu.ShowAsContext();

把这个菜单显示出来。

2、 GenericMenu的扩展使用

  如果需要添加很多菜单,当然也是可以一个一个手动的去AddItem,不过GenericMenu的自由度是比MenuItem高很多的,所以我们也可以根据实际情况来配表显示,或者用数组的方式来添加。
  比如我现在指定一个简单的规则,我这里只有2级菜单,每个根菜单使用一个字符串数组来表示。

    private string[] menu1 = new string[] { "测试菜单1", "测试菜单1-1", "测试菜单1-2", "测试菜单1-3", "测试菜单1-4" };private string[] menu2 = new string[] { "测试菜单2", "测试菜单2-1", "测试菜单2-2", "测试菜单2-3" };private string[] menu3 = new string[] { "测试菜单3", "测试菜单3-1", "测试菜单3-2"};private string[] menu4 = new string[] { "测试菜单4"};

  是这样的规则,我就可以写个通用的添加菜单方法,把所有菜单生成出来:
比如这样:

private GenericMenu menu;
private string[] menu1 = new string[] { "测试菜单1", "测试菜单1-1", "测试菜单1-2", "测试菜单1-3", "测试菜单1-4" };
private string[] menu2 = new string[] { "测试菜单2", "测试菜单2-1", "测试菜单2-2", "测试菜单2-3" };
private string[] menu3 = new string[] { "测试菜单3", "测试菜单3-1", "测试菜单3-2"};
private string[] menu4 = new string[] { "测试菜单4"};
private string currentStr = "";
private void ShowPopMenu2()
{if (GUI.Button(new Rect(0, 50, 100, 30), "test2")){ShowGenericMenu();}}private void ShowGenericMenu()
{if(menu==null){CreateMenu();}menu.ShowAsContext();
}private void CreateMenu()
{menu = new GenericMenu();CreateSubMenu(menu1);menu.AddSeparator("");CreateSubMenu(menu2);menu.AddSeparator("");CreateSubMenu(menu3);menu.AddSeparator("");CreateSubMenu(menu4);
}private void CreateSubMenu(string[] strs)
{if(strs == null||strs.Length==0){return;}string key = strs[0];List<string> subStrList = new List<string>();if(strs.Length>1){for(int i = 1;i<strs.Length;i++){AddToMenu(key + "/" + strs[i], strs[i]);}}else{AddToMenu(key, key);}
}private void AddToMenu(string path,string content)
{menu.AddItem(new GUIContent(path), false, OnSelectMenu,content);
}private void OnSelectMenu(object val)
{currentStr = (string)val;//后续处理
}

  这样,后续需要修改菜单的选项,就可以单纯的修改定义的变量就可以了。
  当然,不同的规则会让生成的代码不一样。我这里只是2层菜单,如果是多层的,只要想好生成的逻辑,都可以统一生成。
  如果想实现选中了就打钩的效果,可以在上面的基础上修改一下:

    private GenericMenu menu;private string[] menu1 = new string[] { "测试菜单1", "测试菜单1-1", "测试菜单1-2", "测试菜单1-3", "测试菜单1-4" };private string[] menu2 = new string[] { "测试菜单2", "测试菜单2-1", "测试菜单2-2", "测试菜单2-3" };private string[] menu3 = new string[] { "测试菜单3", "测试菜单3-1", "测试菜单3-2"};private string[] menu4 = new string[] { "测试菜单4"};private string currentStr = "";private void ShowPopMenu2(){if (GUI.Button(new Rect(0, 50, 100, 30), "test2")){ShowGenericMenu();}}private void ShowGenericMenu(){if(menu==null){CreateMenu();}menu.ShowAsContext();}private void CreateMenu(){menu = new GenericMenu();CreateSubMenu(menu1);menu.AddSeparator("");CreateSubMenu(menu2);menu.AddSeparator("");CreateSubMenu(menu3);menu.AddSeparator("");CreateSubMenu(menu4);}private void CreateSubMenu(string[] strs){if(strs == null||strs.Length==0){return;}string key = strs[0];List<string> subStrList = new List<string>();if(strs.Length>1){for(int i = 1;i<strs.Length;i++){AddToMenu(key + "/" + strs[i], strs[i]);}}else{AddToMenu(key, key);}}private void AddToMenu(string path,string content){menu.AddItem(new GUIContent(path), currentStr == content, OnSelectMenu,content);}private void OnSelectMenu(object val){currentStr = (string)val;CreateMenu();}

  主要是修改了在AddItem的时候,对比了传入的content是否和当前选择的currentStr相等,然后在OnSelectMenu回调时候,重新创建了一下菜单而已。
在这里插入图片描述

3、 优缺点

1. 优点

  很明显,这个方式显示和逻辑方面都比较清晰,也可以通过规则统一生成和处理回调,感觉比较的规范。

2. 缺点

  对比起MenuItem来说,GenericMenu的代码复杂程度会高一点,没有那么直观。不过我感觉也只是相对而已。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/58163.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++二级题 计算好数:1数大于0数(二进制的位运算)

1、题目 若将一个正整数化为二进制数&#xff0c;在此二进制数中&#xff0c;我们将数字1的个数多于数字0的个数的这类二进制数称为好数。 例如&#xff1a; (13)10 (1101)2&#xff0c;其中1的个数为3&#xff0c;0的个数为1&#xff0c;则此数是好数&#xff1b; (10)10 (1…

Nginx upstream

什么是Nginx upstream&#xff1f; Nginx 模块一般分为三大类&#xff1a;handler、filter和upstream。 利用 handler、filter 这两个模块&#xff0c;可以使 Nginx 轻松完成任何单机工作。 upstream 模块将使 Nginx 跨越单机的限制&#xff0c;完成网络数据的接收、处理和转…

【数学二】常微分方程-一阶微分方程

考试要求 1、了解微分方程及其阶、解、通解、初始条件和特解等概念. 2、掌握变量可分离的微分方程及一-阶线性微分方程的解法,会解齐次微分方程. 3、会用降阶法解下列形式的微分方程: y ( n ) f ( x ) , y ′ ′ f ( x , y ′ ) y^{(n)}f(x),y^{}f(x,y^{}) y(n)f(x),y′′f(…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-23

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-23 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-23目录1. Advancements in Visual Language Models for Remote Sensing: Datasets, Capabilities, and Enhancement Techniques摘…

git restore恢复删除文件

新版本 在 Git 2.23 版本之后&#xff0c;Git 引入了一个新的命令 git restore&#xff0c;用于简化文件恢复操作。可以用 git restore 来恢复误删除的文件。下面是详细的使用方法&#xff1a; 1. 恢复工作区中删除的文件&#xff08;未提交&#xff09; 如果文件已被删除&a…

对角双差速轮AGV平移、直行、转弯、原地旋转案例

在对角两对双差速轮 AGV(自动导引车)中,车辆配置了两对差速轮,分别安装在左前(Front Left,FL)和右后(Rear Right,RR)。这种配置使得 AGV 具有较高的机动性,可以实现平移、直行、转弯和原地旋转等复杂运动 2. 运动学模型 2.1 定义变量 车辆参数: r:车轮半径(米…

flask服务通过gunicorn启动

使用 Gunicorn 启动 Flask 服务通常可以提升 Flask 应用的性能。以下是通过 Gunicorn 启动 Flask 服务的步骤&#xff1a; 1. 安装依赖 首先&#xff0c;确保已安装 Flask 和 Gunicorn&#xff1a; pip install flask gunicorn2. 创建 Flask 应用 创建一个简单的 Flask 应用…

IPV6扩展头部

IPv6扩展头部&#xff08;Extension Header&#xff09;是一种用于在IPv6数据包中添加额外信息和功能的结构。与IPv4相比&#xff0c;IPv6的头部设计简化了基本头部&#xff0c;使其更灵活。扩展头部用于提供各种功能&#xff0c;如路由、分片、流量控制等。扩展头部的使用使得…

安全知识见闻-网络安全热门证书

一、OSCP(Offensive Security Certified Professional) 1. 证书介绍 2.考点 3.部分考试要求 4.练习方法 二、OSEP(Offensive Security Exploit Developer) 1.证书介绍 2.考点 3.练习方法 三、CISSP&#xff08;Certified lnformation Systems Security Professional&a…

技术成神之路:二十三种设计模式(导航页)

设计原则/模式链接面向对象的六大设计原则技术成神之路&#xff1a;面向对象的六大设计原则创建型模式单例模式建造者模式原型模式工厂方法模式抽象工厂模式行为型模式策略模式状态模式责任链模式观察者模式备忘录模式迭代器模式模板方法模式访问者模式中介者模式命令模式解释器…

【已解决,含泪总结】非root权限在服务器上配置python和torch环境,代码最终成功训练(一)

配置Python环境 没有root权限服务器上有多个python环境但没有自己想要的怎么办 之前跑别的实验的时候改过指定的python3.7版本&#xff0c;但是居然我过了一段时间之后&#xff0c;再次打开&#xff0c;python版本居然又回到2.7&#xff08;服务器/usr/下的默认python版本&am…

什么是AI神经网络?

文章目录 神经网络的基本概念如何工作&#xff1f;训练过程应用实例未来展望推荐阅读文章 在当今的科技时代&#xff0c;人工智能&#xff08;AI&#xff09;已经深入到我们生活的各个方面&#xff0c;而神经网络则是推动这一发展的重要技术之一。无论是在图像识别、自然语言处…

Zig 语言通用代码生成器:逻辑,冒烟测试版发布二

Zig 语言通用代码生成器&#xff1a;逻辑&#xff0c;冒烟测试版发布二 Zig 语言是一种新的系统编程语言&#xff0c;其生态位类同与 C&#xff0c;是前一段时间大热的 rust 语言的竞品。它某种意义上的确非常像 rust&#xff0c;尤其是在开发过程中无穷无尽抛错的过程&#x…

高等数学-宋浩版2.0-映射

映射&#xff1a;X,Y为非空集合&#xff0c;存在法则F,对X(原像)中每个元素X&#xff0c;按法则F&#xff0c;在Y中有唯一元素与之对应&#xff0c;F为x到Y&#xff08;镜像&#xff09;的映射。f:X->Y X原像&#xff0c;Y像&#xff0c;x定义域&#xff0c;Df,Rf &#x…

Mac book英特尔系列?M系列?两者有什么区别呢

众所周知&#xff0c;Mac book有M系列&#xff0c;搭载的是苹果自研的M芯片&#xff0c;也有着英特尔系列&#xff0c;搭载的是英特尔的处理器&#xff0c;虽然从 2020 年开始&#xff0c;苹果公司逐步推出了自家研发的 M 系列芯片&#xff0c;并逐渐将 MacBook 产品线过渡到 M…

python之多任务爬虫——线程、进程、协程的介绍与使用(16)

文章目录 1、什么是多任务?1.1 进程和线程的概念1.2 多线程与多进程的区别1.3 并发和并行2、python中的全局解释器锁3、多线程执行机制4、python中实现多线程(threading模块)4.1 模块介绍4.2 模块的使用5、python实现多进行程(Multiprocessing模块)5.1 导入模块5.2 模块的…

Caffeine本地缓存框架

Caffeine本地缓存框架 hi&#xff0c;我是阿昌&#xff0c;今天记录一下Java最强本地缓存Caffeine 1、缓存介绍 缓存(Cache)&#xff0c;在软件无处不在。从底层CPU多级缓存&#xff0c;再到客户页面缓存&#xff0c;和服务器数据缓存&#xff0c;导出都存在着缓存的身影&am…

【Nas】X-DOC:Mac mini 安装 ZeroTier 并替换 planet 实现内网穿透

【Nas】X-DOC&#xff1a;Mac mini 安装 ZeroTier 并替换 planet 实现内网穿透 1、下载客户端 ZeroTier One2、安装过程3、更换planet备份原planet4、重启服务5、加入网络6、NAT内网穿透 1、下载客户端 ZeroTier One https://www.zerotier.com/download/ 选择 MacOS 适用版本&…

设计模式: Pimpl(Pointer to Implementation)

这种设计模式通常被称为 Pimpl&#xff08;Pointer to Implementation&#xff09;惯用法&#xff0c;有时也被称为 Cheshire Cat 惯用法。它主要用于隐藏实现细节和减少编译依赖。 例子&#xff1a; DatabaseConnection.h #ifndef DATABASE_CONNECTION_H #define DATABASE_…

Next.js + Prisma + Auth.js 实现完整的认证方案

前言 在现代 Web 应用中,用户认证是一个基础且重要的功能。本文将介绍如何使用 Next.js Prisma Auth.js 实现一个完整的认证方案。这个方案既安全又灵活,能满足大多数项目需求。 技术栈选择 • Next.js: React 全栈框架,提供了服务端渲染和 API 路由• Prisma: 现代数据库…