Search Unity

Touch Press pass through all UI elements

Discussion in 'UGUI & TextMesh Pro' started by charles_cloudcade, Oct 8, 2014.

  1. charles_cloudcade

    charles_cloudcade

    Joined:
    Oct 8, 2014
    Posts:
    2
    I'm having this issue with IsPointerOverGameObject(). Whenever I use it to know if my mouse click is over a GameObject it works fine, but if I want to know if my touch press is over a GameObject it doesn't work.

    This works fine
    Code (CSharp):
    1.  
    2. private void OnMouseDown()
    3. {
    4.    if(UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
    5.    {
    6.          //Do stuff
    7.    }
    8. }
    9.  
    This always return false
    Code (CSharp):
    1.  
    2. private void OnMouseDown()
    3. {
    4.    Touch[] touches = Input.touches;
    5.    foreach (Touch touch in touches)
    6.    {
    7.        if(UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject(touch.fingerId))
    8.        {
    9.             //Do stuff
    10.             break;
    11.        }
    12.    }
    13. }
    14.  
     
    Last edited: Oct 8, 2014
  2. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,225
    This is working in our test cases, please raise a bug if you think you have found an issue.
     
  3. charles_cloudcade

    charles_cloudcade

    Joined:
    Oct 8, 2014
    Posts:
    2
    I've raised a bug. It isn't working on any touch device I've tried.
     
  4. PentagramPro

    PentagramPro

    Joined:
    Apr 5, 2014
    Posts:
    23
    I have the same issue. I use this to block input when user taps on UI:
    Code (CSharp):
    1. if(EventSystem.current.IsPointerOverGameObject())
    2.             return;
    On PC with mouse it works fine, but on Android it doen`t. Unity beta 20.

    P.S. Where is link to this bug on issure tracker? I want to vote for it.
     
  5. blockimperium

    blockimperium

    Joined:
    Jan 21, 2008
    Posts:
    452
    Same here. I thought maybe it just wasn't fully implemented on mobile yet. Let me just say, it makes creating a joystick a very very FUN experience.
     
  6. PentagramPro

    PentagramPro

    Joined:
    Apr 5, 2014
    Posts:
    23
    This bug holds release of my game :(
     
  7. Grenk

    Grenk

    Joined:
    Oct 12, 2014
    Posts:
    2
    I was experiencing a similar problem. The source of the problem is that looking at the raw touches, as returned by Input.touches, will include touches that have just ended or been cancelled. The fingerId field of these Touch objects will always return false when passed to IsPointerOverGameObject. My solution was to only look at active touches, like so:

    Code (CSharp):
    1. bool inputIsActive = (currentTouch.phase != TouchPhase.Ended &&
    2.                       currentTouch.phase != TouchPhase.Canceled);
    Once you know the touch is a valid kind of touch event that can be used to detect UI hits, you can then use

    Code (CSharp):
    1. currentEventSystem.IsPointerOverGameObject(currentTouch.fingerId);
    The same function without parameters does seem to always return false, so it can't be used for touch input at the moment.
     
  8. lianaqiang

    lianaqiang

    Joined:
    Apr 24, 2013
    Posts:
    14
    Issues here is exactly what i want to to, i will give it a try. Is there any other way to handle it?
     
  9. Grenk

    Grenk

    Joined:
    Oct 12, 2014
    Posts:
    2
    I actually did come across another method that will let you essentially force mouse behavior in the EventSystem.
    1. Make sure the system is simulating mouse touches:
      Code (CSharp):
      1. Input.simulateMouseWithTouches = true;
    2. On your EventSystem component, remove the TouchInputModule
    3. On the StandaloneInputModule for the same EventSystem, set Allow Activation On Mobile to true.

    I believe this may actually be the intended functionality for the TouchInputModule class. The parameterless method could provide misleading information if it ever returned true in a situation where there may possibly be more than one touch on the screen simultaneously. For example, a game that has the player interact with UI elements and the world via touch would essentially block all input to the world with one finger while another finger was touching a UI element, if the developer relied on the parameterless method and the method returned true if *any* touches were over a UI, even if some aren't. There world be the opposite problem of the touches going through the UI if it returned true only if *all* touches were over an element. What should really happen is the parameterless method should throw a "not supported" type exception or log an error to let the developer know why they need to change something.
     
    Jean-Fabre and ekow like this.
  10. blockimperium

    blockimperium

    Joined:
    Jan 21, 2008
    Posts:
    452
    Whoa, you're going to release something on a very early beta version of Unity?
     
  11. PentagramPro

    PentagramPro

    Joined:
    Apr 5, 2014
    Posts:
    23
    Yes, it is pretty stable already :) I`m on 4.6 since beta 9. However, my release is free open-source, not so risky as commertial. In fact this problem with touches was the only one I stuck with.
     
  12. PentagramPro

    PentagramPro

    Joined:
    Apr 5, 2014
    Posts:
    23
    I have testet both solutions in my project on Android Tabled and neither of them works. What am I doing wrong?
    Clicks still do pass UI on Android and dont pass it on PC.
     
  13. PentagramPro

    PentagramPro

    Joined:
    Apr 5, 2014
    Posts:
    23
    I`ve performed more tests. This is what I see:
    1. Solution with simulateMouseWithTouches works with bugs. First touch to UI on Android still goes through.
    2. Solution like this does not work on Android at all:
    Code (CSharp):
    1. public void OnMouseDown()
    2.     {
    3.         if(EventSystem.current.IsPointerOverGameObject())
    4.             return;
    5.  
    6.         foreach(Touch t in Input.touches)
    7.         {
    8.  
    9.             if(EventSystem.current.IsPointerOverGameObject(t.fingerId))
    10.                 return;
    11.         }
    12.          
    13.         curPanDistance = 0;
    14.         lastMousePos = Input.mousePosition;
    15.         mouseDown = true;
    16.  
    17.     }
    I have uploaded my game to Google Play with my very own hack, blocking touches in critical secions:
    Code (CSharp):
    1. if(Input.mousePosition.x>Screen.width-316)
    2.             return;
    But I still want to find proper solution. Sources of game.
     
    mimiCryMuffin likes this.
  14. PentagramPro

    PentagramPro

    Joined:
    Apr 5, 2014
    Posts:
    23
    Today I`ve received an answer from Unity development team. Here is how to deal with this issue:

    Hi,

    So the issue you are seeing is related to how OnMouseDown works. What happens is this gets executed BEFORE the calls to the EventSystem so at the time of the press the system thinks it is not over anything.

    There is an easy way to fix this though. Replace your Tap Script with this:
    Code (CSharp):
    1.  
    2. using UnityEngine.EventSystems;
    3.  
    4. public class TapController : BaseController, IPointerClickHandler, IDragHandler {
    5.  
    6.         public delegate void TapEvent();
    7.         public event TapEvent OnTap;
    8.  
    9.         public void OnPointerClick(PointerEve
    10. ntData eventData)
    11.         {
    12.                 OnTap();
    13.         }
    14.  
    15.         public void OnDrag(PointerEventData eventData)
    16.         {
    17.                 M.Scroll(-eventData.delta);
    18.         }
    19. }

    And add a Physics 2D Raycaster to the MainCamera and things should work as you expect. What this does is make the TapController use the new event system in 4.6 instead of using the old event system.

    -Tim C
     
  15. tteneder

    tteneder

    Unity Technologies

    Joined:
    Feb 22, 2011
    Posts:
    175
    thanks for the very useful hints.
    i deal with the problem like this now and it works on iOS:
    Code (CSharp):
    1.  
    2. bool isPointerOverGameObject = false;
    3.  
    4. if(EventSystem.current.currentInputModule is TouchInputModule) {
    5.    for(int i=0; i<Input.touchCount; i++) {
    6.      Touch touch = Input.touches[i];
    7.      if( touch.phase != TouchPhase.Canceled && touch.phase != TouchPhase.Ended) {
    8.        if(EventSystem.current.IsPointerOverGameObject( Input.touches[i].fingerId )) {
    9.          isPointerOverGameObject = true;
    10.          break;
    11.        }
    12.      }
    13.    }
    14. } else {
    15.    isPointerOverGameObject = EventSystem.current.IsPointerOverGameObject();
    16. }
    17.  
     
  16. Arkolve

    Arkolve

    Joined:
    Dec 29, 2013
    Posts:
    20
    It doesn't work for me, when I add atti's code on Update() , the bool isPointerOverGameObject never becomes true, even if the button calls the associated function ...
     
  17. blamejane

    blamejane

    Joined:
    Jul 8, 2013
    Posts:
    233
    Can anyone tell me what the "TapController" is? According to the message from the Unity developer we need to replace our "TapController" with the code. I can't find TapController Anywhere.
     
  18. MaLlorente

    MaLlorente

    Joined:
    May 27, 2013
    Posts:
    5
    Hi Guys, in case some have faced the same problem,
    Code (CSharp):
    1. EventSystem.current.IsPointerOverGameObject
    is not working for
    Code (CSharp):
    1. TouchPhase.Ended
    so you need to center on
    Code (CSharp):
    1. TouchPhase.Began
    The code should look like this:
    Code (CSharp):
    1.  
    2. if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
    3.         {
    4.             // Check if finger is over a UI element
    5.             if(EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
    6.             {
    7.                 Debug.Log("Touched the UI");
    8.             }
    9.         }    
    10.  
    As you can check on the manual : http://docs.unity3d.com/ScriptReference/EventSystems.EventSystem.IsPointerOverGameObject.html
     
  19. nicmarxp

    nicmarxp

    Joined:
    Dec 3, 2017
    Posts:
    406
    Sorry for necroposting, but if anyone finds this I'd like to share. I noticed that I got a "touched the ui" at first, then if I held the finger just for another frame, and the UI moved in the background, it ended up detecting me touching the background. So I skipped the TouchPhase part entirely, and this code works for me:

    Code (CSharp):
    1. if (Input.touchCount > 0        {
    2.             // Check if finger is over a UI element
    3.             if(EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
    4.             {
    5.                 Debug.Log("Touched the UI");
    6.             }
    7.         }  
    However if you expect multiple touches, use Atti's code instead, and still remove the TouchPhase part.

    Thanks again, I love Unity :D
     
  20. grimmy

    grimmy

    Joined:
    Feb 2, 2009
    Posts:
    409
    Absolutely ridiculous. Unity can't perform a single UI tap on Android without problems. And why on earth doesn't the blocks raycasts option on the UI element just block the raycast.

    Why do we have to do any of the above solutions? This is VERY basic stuff that any engine should not have issues with. 3 hours trying to find a solution for this so far today. Big joke.

    Is there even a case where a game would want the player to have the environment behind the UI tapped at the same time as the UI? Why is this default behavior?
     
    Last edited: Mar 29, 2018
  21. kopi1996

    kopi1996

    Joined:
    Dec 14, 2017
    Posts:
    1
    var pointer = new PointerEventData(EventSystem.current) {position = Input.mousePosition};
    var raycastResults = new List<RaycastResult>();
    EventSystem.current.RaycastAll(pointer, raycastResults);
    if (raycastResults.Any())
    {
    return;
    }
     
  22. KrakenUpJP

    KrakenUpJP

    Joined:
    Apr 22, 2018
    Posts:
    9
    I'm bumping this because these are my exact feelings right now. Why doesn't block raycast, actually block the raycast? I've been searching and trying all of the recommendations suggested in dozens of threads to no avail. My game object still gets clicked through the canvas that's clearly laying over it. I have canvas group with block raycast checked on every possible canvas above the game object. I'm at my wits end with it and about to say screw it, enjoy the weird feeling of "why did my score go up" when you clicked the button in the achievement popup.
     
  23. knickerbocker

    knickerbocker

    Joined:
    May 8, 2014
    Posts:
    29
    cant believe this is still an issue, this was an issue in 2014 and the pointer over game object function worked, and now it does not!
     
  24. knickerbocker

    knickerbocker

    Joined:
    May 8, 2014
    Posts:
    29
    This function i modified from an above post worked
    The parameter is Input.getTouch(0).position:


    Code (CSharp):
    1. bool CheckPointer(Vector2 pos) {
    2.         var pointer = new PointerEventData(EventSystem.current) { position = pos };
    3.         var raycastResults = new List<RaycastResult>();
    4.         EventSystem.current.RaycastAll(pointer, raycastResults);
    5.         if (raycastResults.Count>0)
    6.         {
    7.             var ui = raycastResults.Find((obj) => obj.gameObject.layer == 5);
    8.             if (ui.gameObject!=null)
    9.             {
    10.                 Debug.Log("touching UI");
    11.                 return true;
    12.             }
    13.             Debug.Log("not touching UI");
    14.             return false;
    15.  
    16.         }
    17.  
    18.         return false;
    19.     }
     
    Spookytooth3D, doq and Wanderer13 like this.
  25. Spookytooth3D

    Spookytooth3D

    Joined:
    Oct 31, 2013
    Posts:
    73
    Working on the same issue. Do you happen to have a full script you could share for this solution? Thanks!

     
  26. HajiyevEl

    HajiyevEl

    Joined:
    Feb 19, 2020
    Posts:
    47
    Sorry for necroposting, but if anyone has the same problems as me i'd like to share.
    After several days of searching found solution :
    Code (CSharp):
    1.      using UnityEngine.EventSystems;
    2.      using System.Collections.Generic;
    3.      private bool IsPointerOverUIObject() {
    4.              PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
    5.              eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    6.          List<RaycastResult> results = new List<RaycastResult>();
    7.          EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
    8.          return results.Count > 0;
    9.      }
    10.  
    11. //Using a simple if statement lets you check if you're over any ui element or the main game.
    12.  
    13.          if (!IsPointerOverUIObject())
    Hope that helps!
    Source
     
  27. aliasgar5253

    aliasgar5253

    Joined:
    Nov 1, 2020
    Posts:
    4
    Thanks alot it worked perfectly on my Android device!
     
    juelzsantana123 likes this.
  28. zacharyaghaizu

    zacharyaghaizu

    Joined:
    Aug 14, 2020
    Posts:
    65
    Yess!! This after putting the if statement in the update works thanks!
     
    juelzsantana123 likes this.
  29. itsjustoneguy

    itsjustoneguy

    Joined:
    Jun 21, 2015
    Posts:
    8

    Wow it actually worked should be unity's default behavior.
     
  30. davidshandor15

    davidshandor15

    Joined:
    Nov 28, 2021
    Posts:
    2
    Code (CSharp):
    1.  
    2. if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
    3.         {
    4.             // Check if finger is over a UI element
    5.             if(EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
    6.             {
    7.                 Debug.Log("Touched the UI");
    8.             }
    9.         }  
    10.  
    UPDATE TO APRIL 2022: In case you are struggling with the same problem now (like me) - This is the solution (use the inner if statement mentioned above).

    Thank you nicmarxp!
     
    Velhinho and juelzsantana123 like this.