Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

IsPointerOverEventSystemObject always returns false on mobile

Discussion in 'UGUI & TextMesh Pro' started by adhdchris, Aug 29, 2014.

  1. adhdchris

    adhdchris

    Joined:
    Nov 13, 2013
    Posts:
    45
    Calling IsPointerOverEventSystemObject works great in unity editor and standalone builds but on mobile devices (Android to be more specific) it always returns false.
    Is this a bug or am I missing something here?
     
  2. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,219
    IsPointerOverEventSystemObject takes an int for the touch id. Mouse is the default (-1) use touch id's to figure out if a certain finger is over a managed object.
     
    Ahmadhp, SomeAlexander and adhdchris like this.
  3. adhdchris

    adhdchris

    Joined:
    Nov 13, 2013
    Posts:
    45
    Thanks Tim!
    I had no idea IsPointerOverEventSystemObject took a touch id, I'll give it another shot.
     
  4. Supergeek

    Supergeek

    Joined:
    Aug 13, 2010
    Posts:
    103
    Is there a reference as to what touch ids are possible? Like, would 0 be first touch or something?

    Also, wouldn't it be easier to just not propagate touches through stuff, or make it a toggle?
     
  5. phil-Unity

    phil-Unity

    Unity UI Lead Developer

    Joined:
    Nov 23, 2012
    Posts:
    1,226
    No references as of yet but -1 if mouse and then the rest are the ids of Touch.fingerId
     
  6. Supergeek

    Supergeek

    Joined:
    Aug 13, 2010
    Posts:
    103
    Anyone care to post a working example?
     
    awsapps likes this.
  7. adhdchris

    adhdchris

    Joined:
    Nov 13, 2013
    Posts:
    45
    The most basic example would be something like this:
    Code (CSharp):
    1. void Update ()
    2.         {
    3.                 if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began) {
    4.                         if (!EventSystemManager.currentSystem.IsPointerOverEventSystemObject (Input.GetTouch (0).fingerId)) {
    5.                                 //Handle Touch
    6.                         }
    7.                 }
    8.         }
     
  8. Bivrost

    Bivrost

    Joined:
    Mar 26, 2014
    Posts:
    80
    Since 4.6 Beta 19, IsPointerOverEventSystemObject does not work anymore. Is this by design or just a bug?

    This is what we were doing before to detect button touches:
    Code (csharp):
    1.  
    2. // ...
    3. EventSystem eventSystem = EventSystem.current;
    4. return ( eventSystem.IsPointerOverEventSystemObject( fingerId )
    5.     && eventSystem.currentSelectedObject != null );
    6.  
    ...were fingerId is something in the range between -1 (mouse button) and Touch.fingerId.

    The console says: "UnityEngine.EventSystems.EventSystem' does not contain a definition for `IsPointerOverEventSystemObject' and no extension method `IsPointerOverEventSystemObject' of type `UnityEngine.EventSystems.EventSystem' could be found..."

    The code worked fine with 4.6 Beta 17 and 18. Is there a workaround?
     
    dude4004 likes this.
  9. adhdchris

    adhdchris

    Joined:
    Nov 13, 2013
    Posts:
    45
    I believe it's been changed to IsPointerOverGameObject(fingerId).
     
    Bivrost likes this.
  10. Bivrost

    Bivrost

    Joined:
    Mar 26, 2014
    Posts:
    80
    Thank you for the quick reply, adhdchris :) I'll try that later. Couldn't find the information anywhere.
     
  11. gruddlebug

    gruddlebug

    Joined:
    Mar 11, 2013
    Posts:
    65
  12. adhdchris

    adhdchris

    Joined:
    Nov 13, 2013
    Posts:
    45
  13. gruddlebug

    gruddlebug

    Joined:
    Mar 11, 2013
    Posts:
    65
    Very true!
     
  14. Bivrost

    Bivrost

    Joined:
    Mar 26, 2014
    Posts:
    80
    That worked. Thanks again. Actually, I read the release notes several times but couldn't find any information about this change. o_O

    EventSystem.currentSelectedObject is also not working anymore. It's called EventSystem.currentSelectedGameObject now.

    So for the sake of completeness, the code to test for UI button touches changed to:
    Code (csharp):
    1. bool IsPointerOverGameObject( int fingerId )
    2. {
    3.     EventSystem eventSystem = EventSystem.current;
    4.     return ( eventSystem.IsPointerOverGameObject( fingerId )
    5.         && eventSystem.currentSelectedGameObject != null );
    6. }
     
  15. Aurecon_Unity

    Aurecon_Unity

    Joined:
    Jul 6, 2011
    Posts:
    241
    Hi Bivrost

    I've been having issues with getting the UI to block raycasts from touch input (on android) and this thread looks like the best lead I have so far to solve it. Here was the thread I made:

    http://forum.unity3d.com/threads/how-to-make-ui-block-raycats-mobile.271978/#post-1820891

    I would really appreciate getting a bit of your expertise on the code you provided above - I'm not quite sure where to put in in my code. I think it's the answer to my problems, I just can't put the pieces in place!

    Thanks a lot
     
  16. adhdchris

    adhdchris

    Joined:
    Nov 13, 2013
    Posts:
    45
    mickyg, just combine my previous answer on getting the touch id with Bivrost's method (use foreach if you need multitouch):
    Code (CSharp):
    1.  
    2. void Update ()
    3. {
    4.                if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began) {
    5.                         if (IsPointerOverGameObject (Input.GetTouch (0).fingerId)) {
    6.                                 Debug.Log("Hit UI, Ignore Touch");
    7.                         } else {
    8.                                 Debug.Log("Handle Touch");
    9.                         }
    10.                }        
    11. }
    12.  
    13. bool IsPointerOverGameObject( int fingerId )
    14. {
    15.     EventSystem eventSystem = EventSystem.current;
    16.     return ( eventSystem.IsPointerOverGameObject( fingerId )
    17.         && eventSystem.currentSelectedGameObject != null );
    18. }
    19.  
     
    Last edited: Nov 19, 2014
  17. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    The fact that you can finally detect touches on a button, is, seriously, one of the biggest advances for Unity in years! At last! Thanks for the clarification here Bivrost, good one
     
    entropicjoey1 likes this.
  18. Aurecon_Unity

    Aurecon_Unity

    Joined:
    Jul 6, 2011
    Posts:
    241
    Thanks a bunch Chris, I had a setting ticked in the Event System (Allow Activation on Mobile Device) that was causing the code to not work. It's solved now and working great - appreciate your quick reply!
     
  19. Deleted User

    Deleted User

    Guest

    I can't make this solution work on 4.6.0f3 Android; was there a regression? Here's my code:

    // Ignore clicks that are over UI buttons or other visible UI elements
    if (Input.touchCount > 0 && EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId)) {
    // ignore click


    but this does not work on Android because IsPointerOverGameObject() is always false. Input.touchCount and Input.GetTouch(0).fingerId all seem to be giving valid results. In fact I even tried calling IsPointerOverGameObject() for every value from -1 through 5 and the result is always false regardless of whether the click was on a UI element or not (in this case a Button).

    Has anyone got this working with the new UI on Android + 4.6.0f3?
     
    Last edited by a moderator: Dec 5, 2014
    MasterChiefLegend likes this.
  20. bluescrn

    bluescrn

    Joined:
    Feb 25, 2013
    Posts:
    641
    Wouldn't it be better if we could do these tests with a simple Vector2 screen position - rather than having to know device-specific details of the touch/click?

    It looks like there's a raycast function - but it wants a PointerEventData as input, not simple coordinates - so it's clearly not intended for this use.

    Edit: Is this function returning true if the pointer is over *any* canvas object now? - I seem unable to create HUD elements that are ignored by this test, settings on Canvas Groups don't seem to change anything...
     
    Last edited: Dec 6, 2014
    Deleted User likes this.
  21. Deleted User

    Deleted User

    Guest

    That certainly sounds easier. If Unity doesn't fix the Android problem quickly I may have to write that as a work around.
     
  22. Deleted User

    Deleted User

    Guest

    On Windows it seems to return true over any canvas object that is drawn, so e.g. a Panel with a background returns true even if it does not interact. If your HUD has a drawn object my guess it will always be true even if parts are transparent. (Actually I kind of like this default behavior, but it would certainly be better if there was an option on Panels etc to control whether they consume UI events or not.)

    Of course on Android nothing returns true so that'd solve your problem ;D

    From an API design viewpoint, I think the real issue is IsPointerOverGameObject() seems to be a hack to make the Input full-screen APIs co-exist with the new UI. In other UI toolkits the solution for catching events that do not hit active UI elements is to have a final full screen UI element that intercepts events but does not draw at all (so it does not waste fill bandwidth even by drawing an invisible transparent layer). Then it naturally receives the events that do not hit other active UI objects. If Unity added that, and clarified the rules for how their UI event consume/propagate works (I can't figure out their docs) then IsPointerOverGameObject() would not be necessary.
     
  23. bluescrn

    bluescrn

    Joined:
    Feb 25, 2013
    Posts:
    641
    My solution to this for now is to do it the old NGUI way. Add box colliders and do a raycast myself - as this IsPointerOverGameObject is currently no use/broken.

    We need a working way to make UI elements completely ignore input, and ideally we need to be able to do manual raycasts into the UI.
     
  24. Deleted User

    Deleted User

    Guest

    Here's a fix that works on both Windows and Android -- turns out it is quite easy to cast the ray manually. Two examples below, the simple one just casts from Input.mousePosition, the other lets you specify a Canvas and screen position.

    Code (CSharp):
    1. /// <summary>
    2. /// Cast a ray to test if Input.mousePosition is over any UI object in EventSystem.current. This is a replacement
    3. /// for IsPointerOverGameObject() which does not work on Android in 4.6.0f3
    4. /// </summary>
    5. private bool IsPointerOverUIObject() {
    6.     // Referencing this code for GraphicRaycaster https://gist.github.com/stramit/ead7ca1f432f3c0f181f
    7.     // the ray cast appears to require only eventData.position.
    8.     PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
    9.     eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    10.  
    11.     List<RaycastResult> results = new List<RaycastResult>();
    12.     EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
    13.     return results.Count > 0;
    14. }
    15.  
    16. /// <summary>
    17. /// Cast a ray to test if screenPosition is over any UI object in canvas. This is a replacement
    18. /// for IsPointerOverGameObject() which does not work on Android in 4.6.0f3
    19. /// </summary>
    20. private bool IsPointerOverUIObject(Canvas canvas, Vector2 screenPosition) {
    21.     // Referencing this code for GraphicRaycaster https://gist.github.com/stramit/ead7ca1f432f3c0f181f
    22.     // the ray cast appears to require only eventData.position.
    23.     PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
    24.     eventDataCurrentPosition.position = screenPosition;
    25.  
    26.     GraphicRaycaster uiRaycaster = canvas.gameObject.GetComponent<GraphicRaycaster>();
    27.     List<RaycastResult> results = new List<RaycastResult>();
    28.     uiRaycaster.Raycast(eventDataCurrentPosition, results);
    29.     return results.Count > 0;
    30. }
    Given that works fine on Android I have no idea why IsPointerOverGameObject() is so busted
     
  25. chazzysb

    chazzysb

    Joined:
    Jan 8, 2015
    Posts:
    6
    Hi, I got around this problem by giving the UI buttons a tag, and then in code:

    If (EventSystem.current.currentSelectedGameObject != null && EventSystem.current.currentSelectedGameObject.tag == "FireButton") return;

    Hope this helps
     
    tamar_fusic and iwillbenice like this.
  26. MasterChiefLegend

    MasterChiefLegend

    Joined:
    Jan 21, 2014
    Posts:
    2
    I've been trying to get IsPointerOverGameObject working in Unity 5 on an Android tablet, and still no success. Thanks mwk888 for useful solution! I was getting close but got in knots trying to get PointerEventData. Tested on Windows 7 and Samsung Note 10.1 running Android 4.1.2.
     
  27. Sang Cha

    Sang Cha

    Joined:
    Jul 26, 2013
    Posts:
    2
    My thanks to adhdchris and mwk888 for sharing the solution for this problem.
    It works well for iOS project as well.
     
  28. Hullabu

    Hullabu

    Joined:
    Oct 23, 2014
    Posts:
    18
    Ok, it's been a long time, but IsPointerOverGameObject still doesn't work on Android. Is Unity team going to do something with it?
     
  29. Chudziutki

    Chudziutki

    Joined:
    May 16, 2014
    Posts:
    3
    I have the same problem with IsPointerOverGameObject. In Update when any touch is down at first time always return false, but in next Update return true. So in my case i move this to LateUpdate and it works for first time always.
     
    Last edited: Aug 19, 2015
    jister likes this.
  30. coeing

    coeing

    Joined:
    Mar 17, 2011
    Posts:
    271
    I've got a very strange problem right now with IsPointerOverGameObject, too.

    Version: Unity 5.1.1f1

    Each time I do a clean build via command line (i.e. the Library folder is recreated), when I check with IsPointerOverGameObject and the finger id while the touch phase is TouchPhase.Began, the result is false.

    When I do another build with the Library folder still in place, the version works fine and IsPointerOverGameObject returns true even in TouchPhase.Began.

    I do the check in the Update method though, not in LateUpdate. Maybe that's the problem? Anyways it should be consistent whether you make the first build or a second, third,... one.

    Does anybody else have a similar problem?
     
  31. dude4004

    dude4004

    Joined:
    Jul 15, 2014
    Posts:
    181
    Thanks, it worked like a charm!
     
    Alima-Studios likes this.
  32. Rufolangus

    Rufolangus

    Joined:
    Jan 4, 2015
    Posts:
    2
    For some reason isPointerOverGameObject always returns false. I'm running unity 4.6.6f2. We are trying to upgrade the project to 4.6.9 and will test if it works. If it does not, I have a small fix that helps in my case. Drop a rectransform covering up all of the touch area. (Our game's UI is around the sides of the screen) and RectransformUtility.RectangleContainsScreenPoint() if true, do whatever, false, don't do anything.
     
  33. Mykhaylo-Hnatyuk

    Mykhaylo-Hnatyuk

    Joined:
    Jul 8, 2012
    Posts:
    20
    1) You have to use fingerId

    Touch touch = Input.touches[0];
    EventSystem.current.IsPointerOverGameObject(touch.fingerId)


    2) In my case IsPointerOverGameObject returns TRUE if(touch.phase == TouchPhase.Began),
    BUT it returns FALSE if(touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)

    So you can remember if you started your touch over UI and cancel it at the end.
     
  34. KrptonCollector

    KrptonCollector

    Joined:
    May 2, 2015
    Posts:
    4
    Did anyone got it working on android
     
  35. phil-Unity

    phil-Unity

    Unity UI Lead Developer

    Joined:
    Nov 23, 2012
    Posts:
    1,226
    Should work the same on android as any other platform.
     
  36. sworderzhang

    sworderzhang

    Joined:
    Jan 26, 2016
    Posts:
    2
    Not yet, I use Unity 5.2.3, IsPointerOverGameObject works different with iOS and Standalone, hence we must forget it
     
  37. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
    seems quite normal that they would work differently on different platforms.
    mobile doesn't have a mouse for you to hover over things :)
    anyway moving the detection code to LateUpdate fixed the "only second touch got detected" problem for me.

    EDIT: nope i was lying, moving to LateUpdate didn't solve it. adding in the touch.fingerID did.
     
    Last edited: Apr 13, 2016
  38. Fattie

    Fattie

    Joined:
    Jul 5, 2012
    Posts:
    476
    Deleted User likes this.
  39. Ali_V_Quest

    Ali_V_Quest

    Joined:
    Aug 2, 2015
    Posts:
    138
    you can use this code to check touch & mouse
    /// <returns>true if mouse or first touch is over any event system object ( usually gui elements )</returns>
    public static bool IsPointerOverGameObject(){
    //check mouse
    if(EventSystem.current.IsPointerOverGameObject())
    return true;

    //check touch
    if(Input.touchCount > 0 && Input.touches[0].phase == TouchPhase.Began ){
    if(EventSystem.current.IsPointerOverGameObject(Input.touches[0].fingerId))
    return true;
    }

    return false;
    }
     
  40. schwindyboo_unity

    schwindyboo_unity

    Joined:
    Apr 29, 2020
    Posts:
    1
    I just wanted to say thank you for this!
     
    Mario-M701 likes this.
  41. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    Unfortunately, IsPointerOverGameObject only behaves as expected when TouchPhase.Began is used and doesn't with any other TouchPhase. This means that you can't check for UI items when you are dragging a 3D object around!

    This works for placing an object when you click and any UI buttons mask out the click:
    Code (CSharp):
    1.  
    2. if (Input.touchCount == 1)
    3. {
    4.                 if (Input.GetTouch(0).phase == TouchPhase.Began)
    5.                 {
    6.                     if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
    7.                     {
    8.                         Plane plane = new Plane(Vector3.up, Vector3.up); //TODO Don't 'new' this on each frame
    9.                         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    10.                         if (plane.Raycast(ray, out float distance)) propBeingPlaced_GameObject.transform.position = ray.GetPoint(distance);
    11.                     }
    12.                 }
    13. }
    But this doesn't work when you drag..it still goes through UI buttons:
    Code (CSharp):
    1.  
    2. if (Input.touchCount == 1)
    3.             {
    4.                 if (Input.GetTouch(0).phase == TouchPhase.Moved)
    5.                 {
    6.                     if (!EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
    7.                     {
    8.                         Plane plane = new Plane(Vector3.up, Vector3.up); //TODO Don't 'new' this on each frame
    9.                         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    10.                         if (plane.Raycast(ray, out float distance)) propBeingPlaced_GameObject.transform.position = ray.GetPoint(distance);
    11.                     }
    12.                 }
    13.             }
     
  42. perf401

    perf401

    Joined:
    Sep 21, 2021
    Posts:
    4
    Sorry for bumping old thread, but i think it's still a problem? And this thread is almost first in google.
    It looks like IsPointerOverGameObject doesnt work well on TouchPhase.Ended and maybe some other phases.
    So if someone figured out good solution to this, please share.

    I've personally found the laziest solution imaginable, that may not work depends on gameplay and touchphase. It's not even a solution, but more like potentially bugged workaround.

    Code (CSharp):
    1.  (Input.GetTouch(0).phase == TouchPhase.Ended && EventSystem.current.currentSelectedGameObject == null)
    2.               {
    3.                   // Gameplay stuff that won't be triggered if any GUI is pressed
    4.               }
    For touch i wanted to NOT do script FollowTouch, if i fingerpress on specific buttons.

    Code (CSharp):
    1.  
    2. private static bool allowed = true;
    3.  
    4. if (Input.touchCount == 1)
    5.         {
    6.             if (EventSystem.current.currentSelectedGameObject == null && allowed)
    7.             FollowTouch();
    8.             if (EventSystem.current.currentSelectedGameObject != null)
    9.             {
    10.                 if (EventSystem.current.currentSelectedGameObject.name == "PauseGear" && EventSystem.current.currentSelectedGameObject.name == "Cross")
    11.                 allowed = false; //
    12.                 else
    13.                 allowed = true;
    14.             }
    15.              
    16.         }
    Weirdly, without bool it didn't work well. Not sure if it's performance heavy to do such bool in Update(), but it works at least.
     
    Last edited: Dec 22, 2021
  43. ignacio-casal

    ignacio-casal

    Joined:
    Aug 21, 2013
    Posts:
    8
    This was it for me! After a couple of hours digging and trying several approaches.

    I was doing things on touch end, detecting if it was a tap (and not drag) and then doing something.
    To solve it I kept a "didTouchBegan" boolean flag that is only set to true if pointer is not over the ui.

    Thanks @Ziplock9000 !
     
    Ziplock9000 likes this.
  44. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    360
    No problem. Just to add this method has been 100% robust for me in the last year.
     
  45. JayadevHaddadi

    JayadevHaddadi

    Joined:
    Nov 9, 2020
    Posts:
    12
    This fix works from me:

    Code (CSharp):
    1.  
    2.      private bool IsPointerOverUIObject() {
    3.          PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
    4.          eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
    5.          List<RaycastResult> results = new List<RaycastResult>();
    6.          EventSystem.current.RaycastAll(eventDataCurrentPosition, results);
    7.          return results.Count > 0;
    8.      }
    9.  
    Got it from:
    http://answers.unity.com/answers/1115473/view.html
     
    jmlsoft, Nit_Ram and shadowfork like this.