'Unity3D: Detect if mouse has clicked on a UI element?

This is a pretty simple question, but it seems something has changed on Unity in the last versions answers I've found on the internet are no longer valid, so here goes nothing:

I got some UI elements, and a "InputController" class which is intended to handle the user input during the game (Input on the controllers are handled through the onclick events).

What I'm looking is for a way to being able to know if the mouse is clicking a UI element to block the execution of my input handling (and avoid the user clicking on "pause" while also the game executes "left button clicked."

Now, most solutions I've fond were a bit messy or used EventSystem.current.IsPointerOverGameObject() (like this one, which was shown when writing this question), which in 2019.4 does not longer appear. So, there's any new way to do this, do I have to make some hacky solution to receive the event from the UI, then block the execution of my code or am I missing something here?



Solution 1:[1]

You should look into interfaces like IPointerEnterHandler and IPointerExitHandler. If you implement these interfaces for your UI elements, you can add the necessary code to the OnPointerEnter and OnPointerExit methods that those interfaces require.

It should be as simple as adding a bool to your InputController such as isInputEnabled and only handling input when that is true. Set it to false OnPointerEnter and true OnPointerExit.

Solution 2:[2]

I spent a good amount of time trying to figure this out as well. I am on Unity 2022.1 using the Input System and UI Toolkit (UI Elements)

The following should help anyone else who is struggling with this to get the behavior they need.

Examine your UI Documents

You don't want your UI Document to always report clicks. So you need to set the picking mode in your UXML documents accordingly. Have a look at the images below,

full areaintended area

In the image on the left, I have a wrapping element that allows me to position the panel at the bottom of the document. By default this element will receive all pointer events. What I actually want is to only receive pointer events inside of the orange area seen in the image on the right.

I can fix this by setting the picking mode of all parent elements to ignore:

enter image description here

Set up the input system

Setting up the new input system is a topic in itself. Refer to the documentation for more information, but in the image you can see I am using a simple button event as a click/tap action:

enter image description here

Set up your script

Next you need to respond to the input action and check if your input is seen by the UI

public class SimpleInput : MonoBehaviour
{
    public Camera ViewCamera;
    private PlayerControls _controls;

    private void OnEnable()
    {
        Assert.IsNotNull(ViewCamera, "ViewCamera cannot be null");

        // This class will vary depending on the name of your Input Action Asset
        _controls = new PlayerControls();
        _controls.Enable();
        _controls.Gameplay.TapAction.performed += OnInputTapAction;
    }

    private void OnInputTapAction(InputAction.CallbackContext obj)
    {
        Vector2 position = Pointer.current.position.ReadValue();
        Ray ray = ViewCamera.ScreenPointToRay(position);

        if (PointerIsUIHit(position))
        {
            Debug.Log("Ui event received");
        }
        else
        {
            // Perform game-world events here
        }
    }

    private bool PointerIsUIHit(Vector2 position)
    {
        PointerEventData pointer = new PointerEventData(EventSystem.current);
        pointer.position = position;
        List<RaycastResult> raycastResults = new List<RaycastResult>();

        // UI Elements must have `picking mode` set to `position` to be hit
        EventSystem.current.RaycastAll(pointer, raycastResults);

        if (raycastResults.Count > 0)
        {
            foreach (RaycastResult result in raycastResults)
            {
                if (result.distance == 0 && result.isValid)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

The helper method PointerIsUIHit was inspired by a conversation found in the Unity Forums which had some insight into how to get this done. It also shed some insight into the frustrating experience of being a Unity Dev.

Hopefully this helps other people struggling to find a proper guide.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 HumanWrites
Solution 2