Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Touch Input on GUI interfering with gameplay in the background (Android)

Discussion in 'UGUI & TextMesh Pro' started by Exe0, Dec 4, 2016.

  1. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    Hi guys. I'm new to Unity and the forum so I don't know whether this thread should be somewhere else, feel free to move it.

    So I'm making this game where the player object is moving to the position were the player pressed on the screen (android device). It's working great. The player is moving to the x-axis coordinate just as wanted. But, there's a little problem.

    I've implemented a button, a raw image in the Canvas and added a Button Script component to it (I've also tried just to add a normal button). Problem is, when pressed, the player is moving towards the x coordinate of that button. And I dont want that. I want the player to stay at the x-coordinate and only move if the touch is outside of the button.

    My question is: How can you make the player object ignore touch input on a GUI Object? Is it possible? Can I make some checks to see what kind of objects the player are touching on the screen? Any hints would be appreciated. :)

    Here is some code, don't know if that could help.
    Input Class:

    Code (CSharp):
    1.    
    2.  
    3. void Update () {  
    4.         if (player.playerIsAlive)
    5.         {
    6.             if (Input.touchCount == 1)
    7.             {
    8.                     if (gameModePressAndWait)
    9.                     {
    10.                        Touch touchen = Input.GetTouch(0);
    11.  
    12.                         float distanceFromCamera = 10.0f;
    13.  
    14.                         Vector3 PressedByPlayer = new Vector3(touchen.position.x, touchen.position.y, distanceFromCamera);
    15.                         Vector3 PressedByPlayerWorldPosition = Camera.main.ScreenToWorldPoint(PressedByPlayer);
    16.  
    17.                         if (touchen.phase == TouchPhase.Began)
    18.                         {
    19.                             player.pressedlocation = PressedByPlayerWorldPosition;
    20.                         }                  
    21.                    }                              
    22.                 }                    
    23.             }
    24.         }
    25.  
    And some code of my Player Object class:

    Code (CSharp):
    1.      
    2. if (playerIsAlive)
    3.         {
    4.             //player is moving up
    5.             GetComponent<Rigidbody2D>().velocity = new Vector2(0, shipSpeedVertical);
    6.  
    7.  
    8.             if (Application.platform == RuntimePlatform.Android)
    9.             {
    10.                 pressedlocation.y = transform.position.y;
    11.                 transform.position = Vector3.MoveTowards(transform.position, pressedlocation, shipSpeedHorizontal * Time.deltaTime);
    12.          
    13.             }
    14.         }
    15.  
    Anything else you need to know? ;)

    Thanks in advance.
     
    Last edited: Dec 4, 2016
    JimmyMacheta and mkusan like this.
  2. DWit

    DWit

    Joined:
    Dec 4, 2016
    Posts:
    25
    UNSEENWorks likes this.
  3. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    Yeah I've tried it, but it's not working. Here's what I did in my Input class:

    Code (CSharp):
    1.  
    2. if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
    3. {
    4.        Debug.Log("Clicked on the UI");
    5. }
    6. else
    7. {
    8.        if (touchen.phase == TouchPhase.Began)
    9.         {
    10.                  player.pressedlocation = PressedByPlayerWorldPosition;
    11.         }
    12. }
    13.  
    Right now the player object is not responding at all. The button is working though, but the player keeps the same x-axis position. What does IsPointerOverGameObject even do? :p I made some google searches but I still don't think I understand.

    Does it check if the touch is over any GameObject in the scene or only UI/GUI elements in the scene? If it checks all kinds of GameObjects I think I can imagine why it's not working, because right now my backgrounds, which are randomly generated, are also GameObjects. And then he enters the first if block since we are pressing an GameObject.

    Gah, I'm confused :)
     
    sidkhalidr97 likes this.
  4. DWit

    DWit

    Joined:
    Dec 4, 2016
    Posts:
    25
    It checks if pointer is over UI elements from new UI system (UnityEngine.UI). It's not working for old GUI system (OnGUI function), but you shouldn't use it anyway (except for editor tools).
     
  5. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    I'm using UnityEngine.UI.

    So wierd.. Even if I press outside the button i get "Clicked on the UI" printed out (see code above). So the method IsPointerOverGameObject thinks my backgrounds GameObjects are UI elements? Maybe something to do with the canvas, that all objects are inside the canvas and therefore it register them as UI elements? (in the hierarchy they are not, there are only four UI elements there). I've no idea..
     
  6. DWit

    DWit

    Joined:
    Dec 4, 2016
    Posts:
    25
    No. It checks with raytracing if it collides with UI element graphics. If it doesn't have renderer it shouldn't detect that object.
     
  7. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    Much appreciated that you are taking your time trying to help me. I'm still not sure though.

    Could you elaborate, what do you mean with "If it doesn't have renderer it shouldn't detect that object.". Both my buttons and my generated backgrounds have Sprite Renderers. Do you need more info? :p
     
  8. DWit

    DWit

    Joined:
    Dec 4, 2016
    Posts:
    25
    You mean both are using Unity UI (backgrounds and button)? So that would explain why IsPointerOverGameObject always returns true. You can disable UI raycasting for background images by disabling RaycastTarget in background UI Sprite (and all other images you don't want UI to know about)..
     
  9. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    I double checked. My buttons inside the canvas has Canvas Renderer, not Sprite Renderer. The backgrounds though, they have Sprite Renderer. Only my buttons are using Unity UI. The backgrounds are not part of the UI. And it's keep returning true when pressed. So wierd.
     
  10. DWit

    DWit

    Joined:
    Dec 4, 2016
    Posts:
    25
    You can check with which UI object eventsystem is colliding. It can only raycast on UI elements, so there must be UI element:

    Code (csharp):
    1.  
    2. //usage example
    3. Debug.Log("Clicked on the UI");
    4. CheckUIObjectsInPosition(Input.GetTouch(0).position);
    5.  
    6. void CheckUIObjectsInPosition(Vector2 position)
    7. {
    8.   UnityEngine.EventSystems.PointerEventData pointer = new UnityEngine.EventSystems.PointerEventData(UnityEngine.EventSystems.EventSystem.current);
    9.   pointer.position = position;
    10.   List<UnityEngine.EventSystems.RaycastResult> raycastResults = new List<UnityEngine.EventSystems.RaycastResult>();
    11.   UnityEngine.EventSystems.EventSystem.current.RaycastAll(pointer, raycastResults);
    12.  
    13.   if (raycastResults.Count > 0)
    14.   {
    15.   foreach (var go in raycastResults)
    16.   {
    17.   Debug.Log(go.gameObject.name, go.gameObject);
    18.   }
    19.   }
    20. }
    21.  
     
    mkusan likes this.
  11. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    Oh, I'm dumb. It was the canvas itself not the backgrounds. Explains why it was always true..

    This
    Code (CSharp):
    1.  
    2. if (results.Count > 0)
    3. {
    4.             foreach(var go in results)
    5.             {
    6.                 Debug.Log("game object name: " + go.gameObject.name + " , go.gameObject: " + go.gameObject);
    7.             }
    8. }
    made this output:
    game object name: weakLazerButton , go.gameObject: weakLazerButton (UnityEngine.GameObject)
    game object name: Canvas , go.gameObject: Canvas (UnityEngine.GameObject)

    It will work if I do it like this in the method (and change to bool return type ofc):
    return results.Count > 1;

    Everything is now working fine. Thank you! :)

    Now.. Wonder how I could exclude the canvas itself in the raycasts, or in IsPointerOverGameObject.
     
  12. DWit

    DWit

    Joined:
    Dec 4, 2016
    Posts:
    25
    Canvas shouldn't be there. I've tested code and it never returned canvas object. Are you sure you don't have some UI Image or other raycast target as component of canvas GameObject?
     
  13. Exe0

    Exe0

    Joined:
    May 6, 2016
    Posts:
    15
    Yes, I found it. It was both a Button and a Text Component attached to the canvas object, for some reason.. Explains it all. Well now I have a better understanding. Thanks man!
     
  14. Rogelio

    Rogelio

    Joined:
    Apr 15, 2014
    Posts:
    2
    I made this solution:


    Code (CSharp):
    1.  
    2.  
    3.     using UnityEngine.EventSystems;
    4.     ...
    5.  
    6.     if (EventSystem.current.currentSelectedGameObject == null) {
    7.                //not toch UI
    8.     }
    9.  
    if you have a panel or something to block only add button component, and changue color tint to none
     
  15. Ilaiya

    Ilaiya

    Joined:
    Sep 10, 2014
    Posts:
    3
    This is the only solution which worked for me! You are a life saver...
     
  16. mstfsgdc

    mstfsgdc

    Joined:
    Jan 25, 2019
    Posts:
    1
    Worked! Thanks a lot.
     
  17. NHals

    NHals

    Joined:
    Dec 23, 2013
    Posts:
    10
    I found a solution which I think should work for mostly every use-case:

    Code (CSharp):
    1.  
    2. using UnityEngine.EventSystems;
    3.  
    4. /// <summary>
    5. /// Checks if the pointer, or any touch is over (Raycastable) UI.
    6. /// WARNING: ONLY WORKS RELIABLY IF IN LateUpdate/LateTickable!
    7. /// </summary>
    8. private bool IsPointerOverUIObject()
    9. {
    10.     if (EventSystem.current.IsPointerOverGameObject())
    11.         return true;
    12.  
    13.     for (int touchIndex = 0; touchIndex < Input.touchCount; touchIndex++)
    14.     {
    15.         Touch touch = Input.GetTouch(touchIndex);
    16.         if (EventSystem.current.IsPointerOverGameObject(touch.fingerId))
    17.             return true;
    18.     }
    19.  
    20.     return false;
    21. }
    22.  
    The important part here is to run this check in LateUpdate (AND/OR) Verify that the Script Execution Order has EventSystem before mostly everything else.
    upload_2020-1-16_20-30-59.png

    I spent too many hours debugging this, so thought I could dump this here for someone else to use.
     
    Last edited: Mar 16, 2020
  18. Gamer98898

    Gamer98898

    Joined:
    Nov 18, 2017
    Posts:
    11
    Thanks,worked for me :)
     
  19. ZaherHa

    ZaherHa

    Joined:
    Jul 28, 2018
    Posts:
    5
    worked for me
     
  20. Monoblanco

    Monoblanco

    Joined:
    Jul 8, 2015
    Posts:
    1
    I adapted what NHals posted into this and I had to add some extra code to get it to work because the end phase of touch was giving false always. Posting it here in case anyone gets the same issue as me.
    Code (CSharp):
    1. private void LateUpdate()
    2.     {
    3.         if (Input.touchCount > 0)
    4.         {
    5.             Touch touch = Input.GetTouch(0);
    6.             if (!EventSystem.current.IsPointerOverGameObject(touch.fingerId) && touch.phase != TouchPhase.Ended)
    7.             {
    8.                 OnGridTouch?.Invoke(PixelPointToTile(touch.position));
    9.             }
    10.         }
    11.     }
     
    tekler likes this.