在Scene窗口编辑UI界面时,当重叠的UI元素较多时,很难点选想要选中的元素,UI Selector工具做了如下功能:右键时弹出一个列表,列举所有包含鼠标当前位置的RectTransform物体,在列表中选择即可选中该UI元素。

实现该功能需要使用Scene View类,本人用的Unity版本是2020.3.16,该版本里显示onSceneGUIDelegate是弃用状态,使用duringSceneGui代替:

using UnityEngine;
using UnityEditor;namespace SK.Framework
{[InitializeOnLoad]public static class UISelector{static UISelector(){SceneView.duringSceneGui += OnSceneGUI;}private static void OnSceneGUI(SceneView sceneView){}}
}

注意使用InitializeOnLoad属性,该属性应用的对象是静态构造函数,它可以保证在编辑器启动的时候调用该构造函数,因此我们在构造函数中使用SceneView类中的duringSceneGui来实现Scene窗口的自定义功能。

首先我们想要在鼠标右键点击时弹出列表,在编辑器环境中的输入使用Event类,下面的代码表示鼠标右键抬起:

var ec = Event.current;
if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp)
{}

列表中列举所有包含当前鼠标位置的Rect Transform,所以要先获取当前加载的场景中的所有Rect Transform组件:

private static void OnSceneGUI(SceneView sceneView)
{var ec = Event.current;if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp){ec.Use();var scenes = GetAllScenes();var groups = scenes.Where(m => m.isLoaded).SelectMany(m => m.GetRootGameObjects()).Where(m => m.activeInHierarchy).SelectMany(m => m.GetComponentsInChildren<RectTransform>()).GroupBy(m => m.gameObject.scene.name).ToArray();}
}
private static IEnumerable<Scene> GetAllScenes()
{for (int i = 0; i < SceneManager.sceneCount; i++){yield return SceneManager.GetSceneAt(i);}
}

获取到所有的RectTransform组件后,判断哪些包含当前鼠标位置,通过Event.current中的mousePosition可以获得当前鼠标位置,但是需要注意,该坐标系中的原点为左上角:

而UGUI中Canvas的坐标系以左下角为原点,因此需要先进行坐标转换,然后再通过Rect Transform Utility类中的RectangleContainsScreenPoint函数可以判断RectTransform是否包含指定位置:

using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections.Generic;
using UnityEngine.SceneManagement;namespace SK.Framework
{[InitializeOnLoad]public static class UISelector{static UISelector(){SceneView.duringSceneGui += OnSceneGUI;}private static void OnSceneGUI(SceneView sceneView){var ec = Event.current;if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp){ec.Use();// 当前屏幕坐标,左上角是(0,0)右下角(camera.pixelWidth,camera.pixelHeight)Vector2 mousePosition = Event.current.mousePosition;// Retina 屏幕需要拉伸值float mult = EditorGUIUtility.pixelsPerPoint;// 转换成摄像机可接受的屏幕坐标,左下角是(0,0,0)右上角是(camera.pixelWidth,camera.pixelHeight,0)mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y * mult;mousePosition.x *= mult;var scenes = GetAllScenes();var groups = scenes.Where(m => m.isLoaded).SelectMany(m => m.GetRootGameObjects()).Where(m => m.activeInHierarchy).SelectMany(m => m.GetComponentsInChildren<RectTransform>()).Where(m => RectTransformUtility.RectangleContainsScreenPoint(m, mousePosition, sceneView.camera)).GroupBy(m => m.gameObject.scene.name).ToArray();}}private static IEnumerable<Scene> GetAllScenes(){for (int i = 0; i < SceneManager.sceneCount; i++){yield return SceneManager.GetSceneAt(i);}}}
}

同时还要处理同名UI元素问题,以及当前加载的场景可能不止一个的情况,如下:

最终通过GenericMenu类实现右键菜单,通过Selection类中activeTransform和EditorGUI Utility类中PingObject实现选中,完整代码:

using UnityEngine;
using UnityEditor;
using System.Linq;
using System.Collections.Generic;
using UnityEngine.SceneManagement;namespace SK.Framework
{[InitializeOnLoad]public static class UISelector{static UISelector(){SceneView.duringSceneGui += OnSceneGUI;}private static void OnSceneGUI(SceneView sceneView){var ec = Event.current;if (ec != null && ec.button == 1 && ec.type == EventType.MouseUp){ec.Use();// 当前屏幕坐标,左上角是(0,0)右下角(camera.pixelWidth,camera.pixelHeight)Vector2 mousePosition = Event.current.mousePosition;// Retina 屏幕需要拉伸值float mult = EditorGUIUtility.pixelsPerPoint;// 转换成摄像机可接受的屏幕坐标,左下角是(0,0,0)右上角是(camera.pixelWidth,camera.pixelHeight,0)mousePosition.y = sceneView.camera.pixelHeight - mousePosition.y * mult;mousePosition.x *= mult;var scenes = GetAllScenes();var groups = scenes.Where(m => m.isLoaded).SelectMany(m => m.GetRootGameObjects()).Where(m => m.activeInHierarchy).SelectMany(m => m.GetComponentsInChildren<RectTransform>()).Where(m => RectTransformUtility.RectangleContainsScreenPoint(m, mousePosition, sceneView.camera)).GroupBy(m => m.gameObject.scene.name).ToArray();var sceneCount = scenes.Count(m => m.isLoaded);var gc = new GenericMenu();var dic = new Dictionary<string, int>();foreach (var group in groups){foreach (var rt in group){var name = rt.name;var sceneName = rt.gameObject.scene.name;var nameWithSceneName = sceneName + "/" + name;var isContains = dic.ContainsKey(nameWithSceneName);var text = sceneCount <= 1 ? name : nameWithSceneName;if (isContains){var count = dic[nameWithSceneName]++;text += " [" + count.ToString() + "]";}var content = new GUIContent(text);gc.AddItem(content, false, () =>{Selection.activeTransform = rt;EditorGUIUtility.PingObject(rt.gameObject);});if (!isContains){dic.Add(nameWithSceneName, 1);}}}gc.ShowAsContext();}}private static IEnumerable<Scene> GetAllScenes(){for (int i = 0; i < SceneManager.sceneCount; i++){yield return SceneManager.GetSceneAt(i);}}}
}

Unity 编辑器开发实战【Scene View】- UI Selector相关推荐

  1. Unity 编辑器开发实战【Custom Editor】- AudioDatabase Editor 音频库编辑器

    本文实现一个音频库的自定义编辑器,效果如图: 开始实现之前,首先简单介绍该音频库模块,音频库类Audio Database继承自Scriptable Object类,是一个可配置的资源文件: 包含的内 ...

  2. Unity 编辑器开发实战【Custom Editor】- 为UI视图制作动画编辑器

    为了更方便地为UI视图添加动画,将动画的编辑功能封装在了UI View类中,可以通过编辑器快速的为视图编辑动画.动画分为两种类型,一种是Unity中的Animator动画,该类型直接通过一个字符串类型 ...

  3. Unity 编辑器开发实战【Custom Editor】- FSM Editor

    本文介绍如何为FSM有限状态机模块实现一个自定义编辑器面板,FSM的详细代码在上一篇文章中有介绍,链接地址: 在Unity中构建FSM有限状态机 下面是最终效果: 首先,自定义一个编辑器面板,需要用到 ...

  4. Unity 编辑器开发实战【Editor Window】- BlendShape调试工具

    Skin Mesh Renderer组件编辑器本身包含BlendShape的调试滑动条,但是当数量较多想要重置时较为麻烦,下面介绍的工具添加了这些调试滑动条的同时,增加了一键重置的功能: 代码如下: ...

  5. Unity 编辑器开发实战【Editor Window】- 关于提高Proto通信协议文件生成效率的考虑

    在项目中使用Protobuf作为通信协议时,需要用到protogen.exe程序将.proto文件编译成.cs文件再导入Unity工程中使用: 例如我们创建一个ProtoTest.proto文件: 然 ...

  6. Unity编辑器开发(五):实战、开发一个AB包编辑器工具

    前言 在我们上一篇(Unity编辑器开发(四):实战.开发一个AB包编辑器工具)的结尾,我们拥有了如下图中那样的一个编辑器窗口: 接下来我们来完成最后部分,也就是包括创建AB包,清理AB包,删除AB包 ...

  7. 《Unity虚拟现实开发实战》——第3章,第3.6节虚拟现实设备的运行原理

    本节书摘来自华章出版社<Unity虚拟现实开发实战>一书中的第3章,第3.6节虚拟现实设备的运行原理,作者[美] 乔纳森·林诺维斯,更多章节内容可以访问云栖社区"华章计算机&qu ...

  8. 《Unity虚拟现实开发实战》——第1章,第1.8节小结

    本节书摘来自华章出版社<Unity虚拟现实开发实战>一书中的第1章,第1.8节小结,作者[美] 乔纳森·林诺维斯,更多章节内容可以访问云栖社区"华章计算机"公众号查看. ...

  9. Unity编辑器开发——组件聚焦问题

    个人学习笔记,如有错误.疑问.建议,欢迎留言. 声明:本文不得以任何形式进行转载.  前言:在进行Unity编辑器开发时,经常遇到一个组件的聚焦问题:可以更改的组件,如果通过其他方式对其进行更新(即不 ...

  10. Unity编辑器开发(七)——Scene界面拓展之Handles

    个人学习笔记,如有错误.疑问,欢迎留言. 声明:本文不得以任何形式进行转载.  前言:在Unity编辑器拓展时,一个很重要的部分便是在Scene界面进行拓展,通过可视化的显示UI控件,并能响应用户的操 ...

最新文章

  1. linux 增量备份镜像,【备份与恢复】合并增量备份与映像副本
  2. 【连载】【FPGA黑金开发板】Verilog HDL那些事儿--12864(ST7565P)液晶驱动(十三)...
  3. Maven私服搭建(Nexus Repository Manager 3)
  4. 华南农业大学c语言期末试题,华南农业大学珠学院C语言期末试卷.doc
  5. 阿里云服务网格 ASM 发布新功能:提供更精细化的服务治理能力
  6. where is argument in check callback passed by framework - SAP UI5学习笔记
  7. java学习(5):全局变量和局部变量
  8. 如何使用网上下载的arcgis工具箱,报错汇总
  9. 二、Python第二课——变量命名规则及字符串变量相关函数
  10. H5页面唤起指定app或跳转到应用市场
  11. java 实现图片转化为PDF
  12. 不为环境所动就能成功——职场人士寓言(3)
  13. Unity零基础到入门 ☀️| 游戏引擎 Unity 从0到1的 系统学习 路线【全面总结-建议收藏】!
  14. GitHub---团队合作
  15. 进一步解读自主可控云CAD:CrownCAD
  16. SecureCRT鼠标快速复制粘贴
  17. 如何改变默认的checkbox样式
  18. 【数据结构基础整理】图--06:克鲁斯卡尔算法详解
  19. 你的电脑是不是github访问一段时间又不能访问了?提示无法访问此网站。教你原理,这次学会就不用再百度
  20. 我的2007-written in BIT

热门文章

  1. cad修改快捷键_【收藏】史上最齐全的CAD大全
  2. 人脸识别,结构光名词记录
  3. [论文导读]Restoring and attributing ancient texts using deep neural networks深度学习复原古希腊铭文
  4. python的扩展名是_python文件扩展名是什么
  5. 金融知识图谱的现状与展望
  6. cdn回源php_简述回源原理和CDN缓存
  7. 传奇源码分析-服务器端
  8. 【新品重磅发布】FOHEART·HF1面部表情捕捉头盔
  9. 使用Termux进行Linux系统的ubuntu版本安装
  10. kernel function