- Home /
How do I block touch events from propagating through Unity.UI ?
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?
Answer by Kiwasi · Sep 26, 2014 at 10:41 AM
There are three ways to do this, as demonstrated in this video tutorial.
Use EventSystem.current.IsPointerOverGameObject
Convert your OnMouseXXX and Raycasts to an EventSystem trigger. Use a physics raycaster on the camera
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.
http://docs.unity3d.com/$$anonymous$$anual/script-PhysicsRaycaster.html
There is also an appropriate version for 2D
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 On$$anonymous$$ouseXXX redundant.
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.
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.
Check out my post at the bottom. simplest fix ever!
Your solution doesn't help in my case.
After more research I figure it out. On Camera > Physics Raycaster > Event mask, I've choose only Default layer. This means that only game object that are on Default layer will catch events. The game objects that I don't want to catch events, I move them on a different layer.
Answer by Fattie · Jan 25, 2015 at 06:25 AM
From 2015 onwards simply do this:
br>
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".)
3) Add a physics raycaster to the camera. One click. Click the event mask, uncheck everything and check only the the "Draw" layer.
4) 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...
@Fattie Trying to follow your 2015 example with IPointerClickHandler ins$$anonymous$$d of IPointerDown/Up, but can't get it working. I followed steps 1 (3d physics raycast) and step 2 with this method on Finger$$anonymous$$ove (but never see any debug to console when I click or touch the screen.
public void OnPointerClick (PointerEventData eventData)
{
Debug.Log("OnPointerClick");
}
@blamejane A little late, but I found I had to implement IPointerDownHandler and IPointerUpHandler to get IPointerClickHandler working.
Answer by jvhgamer · Nov 21, 2017 at 04:13 PM
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!
Answer by krstrobe · Jul 12, 2016 at 05:33 PM
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!
Answer by sefiroths · Aug 17, 2016 at 01:14 PM
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
Hi @sefiroths,
it's a half year ago since I had no time to do anything in Unity, but, I remember that there have been several solutions to such type of problems. I think on the Canvas was an option to avoid underneath objects to be triggered. In my Case I had an $$anonymous$$enu over another, and the underneath button was pressen as soon as you touched the menu. Here you can find the Property Recieves Event, what sets the processing of events by the specific canvas: https://docs.unity3d.com/$$anonymous$$anual/class-Canvas.html
Here you can find CanvasGroup: https://docs.unity3d.com/$$anonymous$$anual/class-CanvasGroup.html There's a Property named Interactable, try using it.
Try some of theses, I'm pretty shure that I also used some of theses in combination with some own script, such as canvasDisabler.
So the best way, if none of those example-Links work, should be disabling the underneath canvas while the image is in front of the canvas?
BR Tell me if it worked and what worked :D