Unity 编辑器开发实战【Scene View】- UI Selector
在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);
}
}
}
}
相关文章
- CentOS下如何从vi编辑器插入模式退出到命令模式
- 欢迎使用CSDN-markdown编辑器
- 在matlab中使用模糊编辑器实现模糊控制器的设计详解
- CSDN MarkDown编辑器支持展示文章封面啦【8月1日】
- LDAP编辑器 LDAPAdmin
- CSDN博客——markdown编辑器使用的一些小技巧
- 为git关联编辑器(比如notepad++) Associating text editors with Git
- 如何安装 Sublime text 编辑器相关的插件
- MD编辑器用法讲解
- js组件在线编辑器插件、图表库插件、文件树插件
- 会声会影字幕编辑器
- 用EditPlus编辑器配置C#开发环境
- linux vim,vi编辑器的基础
- 关于VS2013编辑器的问题
- Ueditor百度编辑器 高度随内容增多而不断增高 问题解决
- bootstrap-markdown编辑器引入