I have several Canvases with many UI elements and need some method which will return true if mouse cursor currently sits on top of some UI element. I have method for this working in old Input system, however not sure how to approach this in the new Input system.
It reads layer from pointerEnter and returns true if it’s UI layer:
/// <summary>
/// CHECK IF MOUSE IS HOVERING OVER UI ELEMENT
/// </summary>
public static bool isHoveringUIElement
{
get
{
mouseOveredObjectName = "";
mouseOveredObjectTag = "";
if (EventSystem.current == null || EventSystem.current.currentInputModule == null)
{
return false;
}
var inputModuleType = EventSystem.current.currentInputModule.GetType();
MethodInfo methodInfo;
_reflectionCache.TryGetValue(inputModuleType, out methodInfo);
if (methodInfo == null)
{
methodInfo = inputModuleType.GetMethod("GetLastPointerEventData", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
_reflectionCache[inputModuleType] = methodInfo;
}
if (methodInfo == null)
{
return false;
}
var eventData = (PointerEventData)methodInfo.Invoke(EventSystem.current.currentInputModule, new object[] { PointerInputModule.kMouseLeftId });
if (eventData != null && eventData.pointerEnter)
{
mouseOveredObjectName = eventData.pointerEnter.name;
mouseOveredObjectTag = eventData.pointerEnter.tag;
return eventData.pointerEnter.layer == 5; // 5 is Unity's UI layer
}
return false;
}
}
using UnityEngine;
using UnityEngine.EventSystems;
public class StandaloneInputModulePlus : StandaloneInputModule
{
// based on solution provided by Numior: https://answers.unity.com/questions/1234594/how-do-i-find-which-object-is-eventsystemcurrentis.html
public static StandaloneInputModulePlus instance;
protected override void Awake()
{
instance = this;
base.Awake();
}
protected override void OnDestroy()
{
instance = null;
base.OnDestroy();
}
public GameObject GameObjectUnderPointer(int pointerId)
{
var lastPointer = GetLastPointerEventData(pointerId);
if (lastPointer != null) return lastPointer.pointerCurrentRaycast.gameObject;
return null;
}
public GameObject GameObjectUnderMouse()
{
return GameObjectUnderPointer(PointerInputModule.kMouseLeftId);
}
public GameObject GameObjectUnderTouch(Touch touch)
{
return GameObjectUnderPointer(touch.fingerId);
}
////////////////////////////////////////////////////////////////////////////
// ..and this is for testing that it works - delete once you see it's working:
////////////////////////////////////////////////////////////////////////////
private GameObject prevGO = null;
public override void UpdateModule()
{
base.UpdateModule();
GameObject go = GameObjectUnderMouse();
if (go != prevGO)
{
Debug.Log("mouse is now over " + ((go == null) ? "nothing" : go.name));
prevGO = go;
}
}
////////////////////////////////////////////////////////////////////////////
}
Then select the EventSystem object in the scene Hierarchy, go to Inspector, switch to Debug:
Then scroll down to StandaloneInputModule, click Script and change it to the replacement:
You can access the functions through a reference, or like this:
GameObject hoveredGameObject = StandaloneInputModulePlus.instance.GameObjectUnderMouse();
if (hoveredGameObject == null)
{
// no object currently under the pointer
} else ..
Ah - indeed, that was lost on me when I read the problem. There’s got to be a way without the RaycastAll …but you can only spend so much time trying to work it out before you’re like why am I still spending time on this lol
Looks like the same thing I learned the approach I’m using from. I actually did try the new input system some time ago but the roadblock turned out to be WebGL on the mobile browser of my phone (Android and Win64 were fine). It was like touch coordinates were inverted in Y (and the “old” input system works fine… so I went with that), dragging up would drag down kind of thing. Maybe that particular issue has been fixed since then… but at this point I’m too far along anyway to change over to the new one anyway without a good reason.
I’ve just concluded ceremony in Project Settings, switched Input Handling from “Both” to “New”.
Ended up with 50 defined Intup Actions in 2 Input Maps. It will still need some tweaking in the future, like this better UI object hovering or that Release interactions fires two times (even if filtered by contex.performed and value) if Input Action has defined more than 1 Binding. But other than that it looks fine.