How do I block touch events from propagating through Unity.UI ?
Asked Answered
V

8

0

There’s a huge basic problem with Unity.

If I touch a UI Panel, Button, etc and there’s a game object underneath it,

the game object receives an input event.

I don’t want this behavior and nobody does. Incredibly it seems UI does not “catch” input events. What to do?

Vicariate answered 6/6, 2023 at 2:23 Comment(0)
S
0

There are three ways to do this, as demonstrated in this video tutorial.

  1. Use EventSystem.current.IsPointerOverGameObject
  2. Convert your OnMouseXXX and Raycasts to an EventSystem trigger. Use a physics raycaster on the camera
  3. Implement the various handler interfaces from the EventSystems namespace. Use a physics raycaster on the camera.

Number one is easiest for a quick and dirty solution. Number three is far more powerful, and I would recommend it as the default for all new projects.

Edit:Totally removed the old answer and put up a new one. Sorry if that’s killed the comments.

Shingles answered 17/7, 2023 at 7:28 Comment(4)

http://docs.unity3d.com/Manual/script-PhysicsRaycaster.html There is also an appropriate version for 2D

Shingles

Your missing it. There are three new components introduced in 4.6 - GraphicsRaycaster. This is used to detect clicks on the UI - PhysicsRaycaster. This is used to detect clicks on colliders - Physics2DRaycaster. This is used to detect clicks on collider2D All if these components process through the EventSystem. The EventSystem will choose which raycaster has priority, depending on rendering order (this is still a little buggy, but it gets better each patch). This means that Physics.Raycast is no longer needed for detecting inputs over a GameObject. It also makes OnMouseXXX redundant.

Shingles

The video link in my answer explains the basics or click blocking. It's enough to give a competent programmer a place to start. I plan to do a complete intro for the event system, but I haven't got to it yet.

Shingles

The no.3 option works like a charm. But I have a question. How can I disable pointer events so they can pass thru an gameobject? In my case I have two gameobject on top of each other, and in some cases I need to catch PointerDown on the bottom one. Thank you.

Bovid
P
0

#From 2015 onwards simply do this:

  1. You’ll have an EventSystem in the hierarchy already.

  2. Add an empty game object with a collider, bigger than the screen. Call the object “draw”. Make the layer “Draw”. In Physics Settings make that layer interact with nothing.

If the camera moves around, you my wish to add this object simply under the camera; it will move with the camera. (If you think about it, this collider represents the “actual glass of the user’s phone”.)

  1. Add a physics raycaster to the camera. One click. Click the event mask, uncheck everything and check only the the “Draw” layer.

  2. Have the following script on the “draw” object. You’re done.

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class FingerMove:MonoBehaviour,
        IPointerDownHandler, IDragHandler, IPointerUpHandler
	{
	public void OnPointerDown (PointerEventData data)
		{
		Debug.Log("FINGER DOWN");
		prevPointWorldSpace =
                theCam.ScreenToWorldPoint( data.position );
		}
	
	public void OnDrag (PointerEventData data)
		{
		thisPointWorldSpace =
               theCam.ScreenToWorldPoint( data.position );
		realWorldTravel =
               thisPointWorldSpace - prevPointWorldSpace;
		_processRealWorldtravel();
		prevPointWorldSpace = thisPointWorldSpace;
		}
     
	public void OnPointerUp (PointerEventData data)
		{
		Debug.Log("clear finger...");
		}

Now if you’re just detecting finger on the “glass” - so, you don’t care about the user touching objects in the scene, you’re just interested in swipes on the glass. (Example, touch to orbit the camera.) Here’s the script - couldn’t be easier …

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class FingerMove:MonoBehaviour, IPointerDownHandler, IDragHandler, IPointerUpHandler
	{
	private Vector2 prevPoint;
	private Vector2 newPoint;
	private Vector2 screenTravel;
	
	public void OnPointerDown (PointerEventData data)
		{
		Debug.Log("FINGER DOWN");
		prevPoint = data.position;
		}

	public void OnDrag (PointerEventData data)
		{
		newPoint = data.position;
		screenTravel = newPoint - prevPoint;
		_processSwipe();
		}

	public void OnPointerUp (PointerEventData data)
		{
		Debug.Log("FINEGR UP...");
		}
	
	private void _processSwipe()
		{
		// your code here
		Debug.Log("screenTravel left-right.. " + screenTravel.x.ToString("f2"));
		}
	
	}

It’s that simple.

It ignores clicks on the UI. Hooray.

Much discussion and examples…

Platinous answered 6/6, 2023 at 2:21 Comment(1)

@Platinous Trying to follow your 2015 example with IPointerClickHandler instead of IPointerDown/Up, but can't get it working. I followed steps 1 (3d physics raycast) and step 2 with this method on FingerMove (but never see any debug to console when I click or touch the screen. public void OnPointerClick (PointerEventData eventData) { Debug.Log("OnPointerClick"); }

Connally
I
0

Everyone! Here is what solved everything for me! All of this scripting and event stuff gave me trouble. So I thought, what if when I bring up the ui I bring up a 3d object with a collider because those block other objects and input. I put a cube under my main canvas and I have my ui showing and hiding on a mouse click. Now the cube shows up as well and it stops clicks! I didn’t want it physically blocking me so I checked the “is trigger” check box on the box collider. I also turned off the mesh renderer so now it is completely invisible and blocks everything. This solved my background click interactions and it solved my ui mouse clicks from locking the mouse back to the center! This was easy easy and works like a charm! At least in the editor. I hope it helps everyone!

Inheritable answered 12/7, 2016 at 17:33 Comment(0)
A
0

Hi there,
if you are working with the UI system you can create a touch-blocker which blocks everything underneath it by using a very simple graphic data type.

Create a new Component with this code:

using UnityEngine;
using UnityEngine.UI;
public class SimpleGraphic : Graphic 
{
	protected override void Start () 
	{
		material.color = Color.white;
	}
}

Now create a GameObject and add the component.

This is a component based on the UI.Graphic type which is the base for the UI.Image or UI.Text types.
It therefore shares the base functionality but does not have the bloated array of tools that these other types provide.
It will still let you choose to allow rayCast or not (block or not).
As an example you can have a G.O. wth this Comp sit at the top of the siblings list. Objects that are arranged below it will appear above it and are ‘clickable’, those arranged above it are not, neither are any ray-cast-targetable parent-nodes.
If you wish to have it act as a full-screen blocker add a RectTransform to your G.O. and use the stretching/anchoring options.
The Color setting is just to visualise that it is actually there. Adjust the alpha for transparency.
Hope this helps

Anteroom answered 16/8, 2016 at 17:3 Comment(0)
L
0

I have tryed all suggestions but for my kind of problem this doesn’t work:
I have an image larger than the screen that I’d like to move dragging the finger moving the camera.
An UI with buttons.

In the camera script I have:

void LateUpdate () 
	{

		if (EventSystem.current.IsPointerOverGameObject ()) {
//		if( EventSystem.current.currentSelectedGameObject != null ) {
			Debug.Log ("wwwwwwwwwwwww");
		}

		if (SystemInfo.deviceType == DeviceType.Handheld) {
			Touch touch;
			if (Input.touchCount == 1 && Input.GetTouch (0).phase == TouchPhase.Moved) {
				touch = Input.GetTouch (0);
				x += touch.deltaPosition.x * xSpeed * 0.02f;
				y -= touch.deltaPosition.y * ySpeed * 0.02f;

that moves the camera.
When I drag the finger starting from an UI element the camera moves!!! AND

EventSystem.current.IsPointerOverGameObject ()) {
if( EventSystem.current.currentSelectedGameObject != null )

detect the element only when the finger is released, if the finger is released outside the UI element
currentSelectedGameObject is not null blocking everything

OnMouseXXX and Raycasts to an EventSystem trigger

should be inside the gameobject that I don’t want to select if is under another object, but I want to block the movement of the camera…
Can someont put me on the right direction?
thanks

Latashalatashia answered 17/8, 2016 at 13:14 Comment(0)
K
0

I made this solution:

 using UnityEngine.EventSystems;
 ...
 
 if (EventSystem.current.currentSelectedGameObject == null) { 
             //not toch UI
 }

if you have a panel or something to block only add button component, and changue color tint to none

Kalb answered 20/3, 2017 at 8:13 Comment(0)
A
0

Just about every single source of information related to this question supplies this line of code as the answer:

EventSystem.current.IsPointerOverGameObject

“Simply add this check. It will return true if over a UI element”

Ok, this is true but, it returns true when over a GameObject as well. Meaning it returns true for UI element clicks and GameObject clicks. Effectively accomplishing the exact opposite of the request by allowing user clicks to propagate through the UI rather then the desired behavior of the UI blocking clicks from passing through it.

I found success in combining the piece above with something like this:

if (UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
{
    if (UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject != null)
    {
        if (UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject.GetComponent<CanvasRenderer>() != null)
        {
          //setting a boolean here, if it was true this means I clicked on a UI element
          clickingGuiElement = true;
        }
        else
        {
          clickingGuiElement = false;
        }
    }
    else
    {
      clickingGuiElement = false;
    }
}
else
{
  clickingGuiElement = false;
}

Then modifying my input code:

if (Input.GetMouseButtonDown(0))
{
    if (Physics.Raycast(ray, out hit))
    {
        if (clickingGuiElement == true)
            return;
        else
            // interaction for GOs not on GUI here
    }
}

Hope this helps!

Acetate answered 6/6, 2023 at 2:31 Comment(1)

Thanks for this, was just what I needed!

Appalachia
S
0

Here’s what I did to solve this problem:

[RequireComponent(typeof(Button))]
public class EventBlocker : MonoBehaviour
{
    void Awake() {
        GetComponent<Button>().onClick.AddListener(OnClick);
    }

    void OnClick() {
        Event.current.Use();
    }
}  

The line in OnClick consumes the event, making all other UI elements ignore it.
Add this script to any UI element that you wish would block touches/clicks.
As you can see, it requires a button, to catch the event.
You can turn transition off, and set the graphic to whatever you want, even transparent, provided it’s still there and has “Raycast Target” checked.

I have no idea why this isn’t default behavior, or built-in, like in Android where buttons return a Boolean indicating if the event was handled or not.
But that’s the best solution to this that I have found so far.

Soluble answered 10/6, 2019 at 15:5 Comment(1)

@Soluble - would you be so kind and list the steps required to implement your solution. This is what I did so far without getting the required behavior: 1. I have a panel and added Button script to it 2. The panel has Raycast Target checked 3. I have added the above script (EventBlocker) to the panel 4. Now, when I click on the panel, OnClick indeed catches the event, however, Event.current is null Please, let me know, what did I do wrong, thanks!

Songful

© 2022 - 2024 — McMap. All rights reserved.