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,一经查实,立即删除!

相关文章

Nginx upstream

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

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

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

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…

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…

HBuilder X 中Vue.js基础使用2(三)

一、条件渲染 1、条件判断 v-if &#xff1a; 表达式返回真值时才被渲染 v-else &#xff1a;表达式返回为假时不被渲染 2、 分支条件判断 v-else-if &#xff1a;使用v-if , v-else-if 和 v-else 来表示其他的条件分支 3、显示隐藏 v-show v-show true 把节点显示 …

PortQry下载安装使用教程(超详细),Windows测试UDP端口

《网络安全自学教程》 PortQry是微软官方提供的一款TCP/IP连接「排障工具」&#xff0c;用来「检查」TCP/UDP「端口状态」。 平时检查端口状态&#xff0c;最常用的是telnet&#xff0c;但它是基于TCP协议的&#xff0c;无法检测「UDP端口」&#xff0c;这篇文章教大家如何在W…

Axure随机验证码高级交互

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;字母数字随机验证码高级交互 主要内容&#xff1a;4位字母数字随机验证码生成、错误提示与State状态同步 应用场景&#xff1a;登录验证码、其他类…

面试宝典(五):用三个线程按顺序循环打印123三个数字,比如123123123

要使用三个线程按顺序循环打印123三个数字&#xff0c;势必要控制线程的执行顺序&#xff0c;可以使用java.util.concurrent包中的Semaphore类来控制线程的执行顺序。 代码示例 import java.util.concurrent.Semaphore;public class SequentialPrinting123 {private static Se…

leetcode:34. 在排序数组中查找元素的第一个和最后一个位置(python3解法)

#1024程序员节 | 征文# 难度&#xff1a;中等 给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(lo…

初识算法 · 前缀和(1)

目录 前言&#xff1a; 一维数组的前缀和 题目解析 算法原理 算法编写 二维数组的前缀和 题目解析 算法原理 算法编写 前言&#xff1a; ​本文的主题是前缀和&#xff0c;通过两道题目讲解&#xff0c;一道是一维数组的模板&#xff0c;一道是二维数组的模板。 链接…

【WebGIS实例】(18)MapboxGL 绘制矢量——线、面

前言 Mapbox GL JS 版本&#xff1a;3.6.0 该博客仅供学习参考&#xff0c;如果您是计划在实际项目中实现该功能&#xff0c;也推荐您直接使用已有的功能库&#xff1a; 官方案例&#xff1a;Draw a polygon and calculate its areamapbox-gl-draw&#xff1a;mapbox/mapbox-g…

基于Django+python的酒店客房入侵检测系统设计与实现

项目运行 需要先安装Python的相关依赖&#xff1a;pymysql&#xff0c;Django3.2.8&#xff0c;pillow 使用pip install 安装 第一步&#xff1a;创建数据库 第二步&#xff1a;执行SQL语句&#xff0c;.sql文件&#xff0c;运行该文件中的SQL语句 第三步&#xff1a;修改源…

HTTPS讲解

前瞻 HTTP与HTTPS的关系 HTTPS也是一个在应用层的协议&#xff0c;是在HTTP协议基础上的一个加密解密层 明文 密文 秘钥 明文->秘钥 加密 秘钥->明文 解密 例如:明文为7 秘钥为2 7^21015&#xff1b; 5就是密文例子: 因为http的内容是明文传输的&#xff0c;明文…

危险物品图像分割系统:一键训练

危险物品图像分割系统源码&#xff06;数据集分享 [yolov8-seg-GFPN&#xff06;yolov8-seg-CSwinTransformer等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AAAI Global…

LabVIEW共享变量通信故障

问题概述&#xff1a; 在LabVIEW项目中&#xff0c;使用IO服务器创建共享变量&#xff0c;并通过LabVIEW作为从站进行数据通信。通讯在最初运行时正常&#xff0c;但在经过一段时间或几个小时后&#xff0c;VI前面板出现错误输出&#xff0c;导致数据传输失败。虽然“分布式系统…

折扣影票接口对接渠道如何选择?

选择折扣影票接口对接渠道需要综合多方面因素考虑&#xff0c;以下是一些建议&#xff1a; 1.合法性和合规性&#xff1a; 确认供应商资质&#xff1a;优先选择具有相关票务经营资质的渠道。比如一些大型的在线票务平台&#xff0c;它们通常经过官方认证和监管&#xff0c;在…

[JAVAEE] 多线程的案例(二) - 阻塞队列 生产者消费者模型

目录 一. 什么是阻塞队列 二. java中的阻塞队列 三. 生产者消费者模型 3.1 生产者消费者模型与阻塞队列密不可分的关系 3.2 阻塞队列在生产者消费者模型的作用 a. 解耦合 b. 削峰填谷 四. 模拟实现阻塞队列 4.1 实现put方法 4.2 实现take方法 4.3 生产者消费者模型​…

了解C# 程序结构

本节我们将学习 C# 编程语言的结构&#xff0c;为了让大家能够对 C# 程序结构有个更好的理解&#xff0c;我们会先演示一个最小的、最简单的 C# 程序结构&#xff0c;以便作为接下来的章节的参考。 C# Hello World 实例 一个 C# 程序主要包括以下部分&#xff1a; 命名空间声明…