I am playing around with the UI Toolkit on a game I am making and loving it so far. The issue I am running into is that the UI does not seem to block raycasts, so the scene items behind the UI still get the OnMouseEnter/OnMouseExit events. Is there a way to toggle this so certain items block raycasts in the new UI?
We donât have a built-in way to do this. Youâll need to implement something on your own using UI Toolkitâs Event System to notify your ray casters when to ignore rays.
Itâs something weâll need to address at some point, yes. But itâs not on our immediate roadmap for 1.0 so I canât really give any estimates on when it will be implemented.
Thank you for the replies, it definitely feels like a basic function of UI to be on even base level parity with old UI system, but for now I will just use a boolean in my scene to determine if the overlay is open and block all mouse enter/exit events.
I am interested in something like this. Can you suggest or point me in the right direction on how to do this? I have more than just menuâs that I need to know if the mouse is over.
You can have an element that takes up the entire screen (using Absolute position and left/right/top/bottom set to 0), sits behind all of the rest of your UI (by just making it be the first child of your rootVisualElement), and have it capture all Mouse events. If an event made it to this âlayerâ element, it means no other element has stopped its propagation (used it), so you can than convert the element to a raycast. Otherwise, you just have raycasts disabled.
What I am asking for is the equivalent of EventSystem.current.IsPointerOverGameObject(). Not sure how the event system will block OnMouseEnter/OnMouseExit, or doing physics queries using the mouse. I would like to block my code from performing those types of operations when the mouse is over the UI.
Right now, you can only know if the mouse is over the UI (and has been used by some UI element) from within the UI Toolkit world - as I roughly described above. This should indeed be equivalent to âEventSystem.current.IsPointerOverGameObject()â if âcurrentâ was your previous uGUI element gameobject. Itâs just a bit more involved right now.
I ended up with a solution similar to what uDamian mentioned above and it works well. Iâm using the new input system and I was having an issue with the order of events when I had an action bound to left click, it would fire the action before UITK handled the event. The way around this was to bind to virtual mouse left click instead, then capture the event in UITK and when the event needed to propagate, fire off a fake virtual mouse action using the UnityEngine.InputSystem.OnScreen.OnScreenControl class. This way I was able to capture UI events correctly.
This works only within one UIDocument it seems.
When you have multiple UIDocuments layered over each other, uielements of one UIDocument donât block events for the UIDocument below.
Starting in Unity 2021.1, we are aiming to make UIToolkit more compatible to use along with partial UI created with the old GameObject-based paradigm (UGUI). As such, UITK runtime panels will be aware of UGUIâs EventSystem through a new selectableGameObject assignable field, which will allow any given runtime panel to be selected by EventSystem.current.currentSelectedGameObject, and to answer positively to EventSystem.current.IsPointerOverGameObject() whenever the mouse is over a VisualElement that has pickingMode == PickingMode.Position.
When associating a selectableGameObject to a runtime panel, by default UGUIâs EventSystem will check for two components: PanelRaycaster, responsible for returning the panelâs information (including sortingOrder) if a ray intersects it, and PanelEventHandler, implementing all UGUI message handlers and rerouting them to UITKâs event loop.
Those features arenât available in Unity 2021.1âs current public alpha, but the alpha that contains them should be coming out shortl. If anyone is interested in playing with it, we will be listening to your feedback of course. Note that none of these features will be forced to be used. If a projects uses UITK and UGUI independently already, we will be providing a method to configure UGUIâs EventSystem to behave as though UITK didnât exist, to preserve backwards compatibility.
I get the concept of this, but how exactly do I do this? I am making an AR app, where the user places the object on a plane using ARRaycast. Once they are happy with the placement, they should hit a âdoneâ button and turn off all the ARPlaneManager stuff. Without blocking, the object jumps to a cast under the button.
I might have to convert this to a uGUI button, but I really like the UI Tool Kit, and would like to use it for everything if possible.
OK, to answer my own question this is super-ugly but it works. Create a fullscreen Visual Element named âscreenâ, which contains the button. The button doesnât get the Pointer events.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.XR.ARFoundation;
public class PlacementInitializer : MonoBehaviour
{
public GameObject thingToPlace;
public UIDocument placementControls;
ARPlaneManager aRPlaneManager;
PlacementController placement;
void Start()
{
aRPlaneManager = FindObjectOfType<ARPlaneManager>();
placement = FindObjectOfType<PlacementController>();
var root = placementControls.rootVisualElement;
root?.Q("done")?.RegisterCallback<ClickEvent>(ev => MakeTheThing());
root?.Q("screen")?.RegisterCallback<PointerDownEvent>(ev => DownOverScreen());
root?.Q("screen")?.RegisterCallback<PointerUpEvent>(ev => UpOverScreen());
}
void DownOverScreen()
{
if (placement)
placement.isOverGameScreen = true;
//Debug.Log("Is Over screen " + Time.time);
}
void UpOverScreen()
{
if (placement)
placement.isOverGameScreen = false;
//Debug.Log("Is Up Over screen " + Time.time);
}
void MakeTheThing()
{
if (placement)
placement.isOverGameScreen = false;
//Debug.Log("Is Over Button " + Time.time);
Instantiate(thingToPlace, transform.position, transform.rotation);
DisablePlanesAndManager();
Destroy(this.gameObject);
}
void DisablePlanesAndManager()
{
if (!aRPlaneManager)
{
Debug.LogError("Got no ARPlane Manager!");
return;
}
aRPlaneManager.enabled = false;
foreach (ARPlane arp in aRPlaneManager.trackables)
{
arp.gameObject.SetActive(false);
}
ARRaycastManager aRRaycastManager = FindObjectOfType<ARRaycastManager>();
if (aRRaycastManager)
aRRaycastManager.enabled = false;
if (placement)
placement.enabled = false;
}
}
I created something like this in our project, only with serveral uidocuments.
Unforunatelly in 2021.1.0b8 this does not work anymore because you canât klick through the invisible Fullscreen VisualElement anymore (the UI document below does not register any clicks because of that)
Running into this same problem. It would be nice for PC games to have a proper integrated solution, but Iâm okay with rolling my own. However, Iâm stuck with an issue of how to pass on mouse events which are blocked by transparent portions of images.
Iâm using both vector graphics to mask images and images with transparency and in both cases the rectangle of the visual element is capturing all mouse events, but I need to be able to only capture those over non-transparent, non-masked pixels so I can pass on the others.
Iâm not sure what the solution is but without one the UI Toolkit is basically unusable for PC games that use the mouse.
2022 and frankly, itâs pathetic that the new input system doesnât handle this basic case. Almost every game has some UI elements that when interacted with should NOT propagate the events further into the 3D scene behind. The old solution with IsPointerOverGameObject was already clunky, but at least it worked. Offer nothing comparable in the new input system is a joke, sorry for being rude but it is.
I positively regret moving my project over to the new input system.
I was planing to jump into UIToolkit.
Now Iâm starting to worry, can you please answer one question.
Can we check if cursor is over GUI elements ? (flag,event,anything )
According to UI support | Input System | 1.2.0 the recommended ways are essentially âbuild your game so this problem doesnât appearâ or âpoll the InputSystem in Update()â (which defeats half of the point of using it, but hey) or âuse EventSystem.
RaycastAllâ (because the stupid Input System didnât already make TWO raycasts (graphics and physics), so letâs make the exact same ones again, because why not?
Is it really so frigging difficult to store âcursor is currently over UI elementâ somewhere when youâre doing graphics raycast every frame anyways?
You can use the EventSystem, they made a version that works with UI elements. So you get the âclunkyâ IsPointerOverGameObject() with UI Elements. It also makes it work with the new Input System. The link you posted actually states that you can use it. Itâs a solution that works without any extra ray casts.