How to make gameplay ignore clicks on UI Button in Unity3D?
Asked Answered
M

3

12

I have a UI Button (using UnityEngine.UI).

However, clicking on the Button seems to be clicking through onto the scene (in my case clicking a nav mesh).

How to solve this problem?

I've been using typical Unity3D code to get user in put in gameplay such as

if (Input.GetMouseButtonDown(0))
  {

same if I try the approach

if( Input.touches.Length > 0 )
        {

        if ( Input.touches[0].phase == TouchPhase.Began )
            {

and it seems to be the case on iOS, Android, and desktop.

It seems to be a basic problem that clicks on the UI (UnityEngine.UI.Button etc) seem to fall through to the gameplay.

Mambo answered 20/2, 2016 at 22:36 Comment(1)
@JoeBlow If it has been asked before on SO then mark it as duplicate, otherwise this appears to be on-topic (a specific programming problem that may be helpful to others)Verbify
P
28

Here's how you do it in Unity today:

  1. Naturally you'll have an EventSystem in the hierarchy - just check that you do. (You get one of those automatically when, for example, you add a Canvas; usually, every scene in an Unity project already has an EventSystem, but just check that you do have one.)

  2. Add a physics raycaster to the camera (that takes one click)

  3. Do this:

.

  using UnityEngine.EventSystems;
  public class Gameplay:MonoBehaviour, IPointerDownHandler {
   public void OnPointerDown(PointerEventData eventData) {
    Bingo();
    }
   }

Basically, again basically, that is all there is to it.

Quite simply: that is how you handle touch in Unity. That's all there is to it.

Add a raycaster, and have that code.

It looks easy and it is easy. However, it can be complicated to do well.


(Footnote: some horrors of doing drags in Unity: Horrors of OnPointerDown versus OnBeginDrag in Unity3D )


Unity's journey through touch technology has been fascinating:

  1. "Early Unity" ... was extremely easy. Utterly useless. Didn't work at all.

  2. "Current 'new' Unity" ... Works beautifully. Very easy, but difficult to use in an expert manner.

  3. "Coming future Unity" ... Around 2025 they will make it BOTH actually work AND be easy to use. Don't hold your breath.

(The situation is not unlike Unity's UI system. At first the UI system was laughable. Now, it is great, but somewhat complex to use in an expert manner. As of 2019, they are about to again totally change it.)

(The networking is the same. At first it was total trash. The "new" networking is/was pretty good, but has some very bad choices. Just recently 2019 they have changed the networking again.)


Handy related tip!

Remember! When you have a full-screen invisible panel which holds some buttons. On the full-screen invisible panel itself, you must turn off raycasting! It's easy to forget:

enter image description here


As a historic matter: here is the rough-and-ready quick-fix for "ignoring the UI", which you used to be able to use in Unity years ago...

if (Input.GetMouseButtonDown(0)) { // doesn't really work...
  if (UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
      return;
  Bingo();
  }

You cannot do this any more, for some years now.

Polyptych answered 20/2, 2016 at 23:20 Comment(4)
I researched the problem before posting the question but couldn't find anything. Obviously wasn't using the correct keywords. Thanks for the solution. Worked a charm.Mambo
@Polyptych - would you be so kind and describe your solution step by step as it can be recently implemented? I have added physics raycaster to the camera but the OnClick button event is still propagated to the gameplay/scene...Angloamerican
omgness, there are 5bajillion articles saying to use EventSystem.current.IsPointerOverGameObject which got me no where, but this worked perfectly! Thanks!Poult
hi @Poult - right, a huge problem with Unity is there are a huge number of out of date articles out there. it's tricky. note too that they are right now introducing yet ANOTHER "totally new" UX system :/ WTH right?Polyptych
O
5

I had this problem too and I couldn't find very much useful info on it, this is what worked for me:

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

public class SomeClickableObject : MonoBehaviour
{
    // keep reference to UI to detect for
    // for me this was a panel with some buttons
    public GameObject ui;

    void OnMouseDown()
    {
        if (!this.IsPointerOverUIObject())
        {
            // do normal OnMouseDown stuff
        }
    }

    private bool IsPointerOverUIObject()
    {
        // get current pointer position and raycast it
        PointerEventData eventDataCurrentPosition = new PointerEventData(EventSystem.current);
        eventDataCurrentPosition.position = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventDataCurrentPosition, results);

        // check if the target is in the UI
        foreach (RaycastResult r in results) {
            bool isUIClick = r.gameObject.transform.IsChildOf(this.ui.transform); 
            if (isUIClick) {
                return true;
            }
        }
        return false;
    }
}

Essentially each click checks if the click occurred over a UI target.

Outstation answered 3/12, 2020 at 13:54 Comment(1)
Kind of a hack to perform the same raycast again which the UI system also performs on its own, but as far as I can tell there is no better way to get the EventSystem to tell you whether or not its own raycast hit something.Termination
M
0

Unfortunately, the first recommendations didn't help me so I just five all panels tags "UIPanel" and when the mouse clicked check if any panel currently active

    void Update()
    { if (Input.GetMouseButtonDown(0))
        {
            if (isPanelsOn()) 
            {}//doing nothing because panels is on
            else 
            {} //doing what we need to do
         }
}
public static bool isPanelsOn()
    {
        GameObject[] panels = GameObject.FindGameObjectsWithTag("UIPanel");
        foreach (var panel in panels)
        {
           if (panel.activeSelf)
            {
                return true; 
            }
        }
        return false;
    }
Moonlit answered 28/3, 2021 at 19:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.