- Home /
Checking UI element beneath cursor in editor
I have been attempting to write a method that will return the object beneath the cursor in the Scene View of the editor. Using the following very neatly works for common game objects in the scene, such as models and primitives.
Ray ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
object foundObject = HandleUtility.RaySnap(ray);
or
GameObject foundObject = HandleUtility.PickGameObject(Event.current.mousePosition, false);
HOWEVER it does NOT work for finding canvases and UI elements in the scene. As far as I can tell, the Graphic Raycaster does not work for this purpose either.
When you use the scene view normally, you can click on elements of a canvas from the scene view camera and select them just fine. This implies that detecting them in an editor script is possible, but no search that I've done has come up with anything that suggests how to do it!
Here is my test code, as well as an image of the simple scene that it's supposed to work on.
[InitializeOnLoad]
public class RaycastTest{
static Event e { get { return Event.current; } }
static RaycastTest() {
SceneView.duringSceneGui += GUIUpdate;
}
static void GUIUpdate(SceneView sceneview) {
switch (e.type) {
//case EventType.Repaint:
// break;
case EventType.MouseDown:
TestCast();
break;
//case EventType.MouseUp:
// break;
//case EventType.MouseDrag:
// break;
//case EventType.Layout:
// break;
}
}
static void TestCast() {
//Ray ray = HandleUtility.GUIPointToWorldRay(e.mousePosition);
//HandleUtility.RaySnap(ray);
//HandleUtility.PickGameObject(Event.current.mousePosition, false);
RaycastHit2D hit = Physics2D.Raycast(Camera.current.ScreenToWorldPoint(e.mousePosition), Vector2.zero);
Debug.Log(hit.collider);
}
}
Please help!
Answer by Gabriel_Ris · Oct 28, 2021 at 07:40 PM
I came up with a solution for this based on how the GraphicsRaycast works. You can use check which elements are underneath the mouse and print the one on the front. Cheers!
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
[InitializeOnLoad]
public class RaycastTest
{
static Event e { get { return Event.current; } }
private readonly static Canvas canvas;
static RaycastTest()
{
canvas = Object.FindObjectOfType<Canvas>();
SceneView.duringSceneGui += GUIUpdate;
}
static void GUIUpdate(SceneView sceneview)
{
switch (e.type)
{
//case EventType.Repaint:
// break;
case EventType.MouseDown:
TestCast();
break;
//case EventType.MouseUp:
// break;
//case EventType.MouseDrag:
// break;
//case EventType.Layout:
// break;
}
}
static void TestCast()
{
Camera sceneCamera = SceneView.lastActiveSceneView.camera;
Graphic frontGraphic = null;
// Get mouse screen position
Vector3 mouseScreenPosition = HandleUtility.GUIPointToScreenPixelCoordinate(e.mousePosition);
// Get the list of all graphics in the scene
IList<Graphic> graphics = GraphicRegistry.GetRaycastableGraphicsForCanvas(canvas);
// Get the front-most graphic on the position you clicked
for (int i = 0; i < graphics.Count; i++)
{
Graphic graphic = graphics[i];
if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
continue;
if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, mouseScreenPosition, sceneCamera, graphic.raycastPadding))
continue;
if (sceneCamera != null && sceneCamera.WorldToScreenPoint(graphic.rectTransform.position).z > sceneCamera.farClipPlane)
continue;
if (graphic.Raycast(mouseScreenPosition, sceneCamera))
{
if (frontGraphic == null || frontGraphic.depth < graphic.depth)
{
frontGraphic = graphic;
}
}
}
if (frontGraphic != null)
{
Debug.Log("You are clicking on " + frontGraphic.name);
}
}
}
This looks excellent! I will give it a shot later today and verify that it works.
Answer by Steyo · Sep 10, 2020 at 12:55 PM
Try with a raycasthit2D :
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(eventData.position), Vector2.zero);
It worked fine for me.
EDIT : eventData being the event got from the OnDrag fonction in my case.
This doesn't work for my purpose, since I'm trying to pick a UI element, rather than an object with a Collider2D component. Editing my question with some code too.