Is there a way to block raycasts?

Hello all,

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?

Thanks!

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.

Will there be an official release eventually to do this?

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.

1 Like

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.

Thanks!

1 Like

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.

1 Like

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.

1 Like

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;
    }

}
1 Like

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.