如何在Unity3D中使游戏玩法忽略UI按钮的点击?

12

我有一个UI按钮(使用UnityEngine.UI)。

然而,单击该按钮似乎会穿透到场景(在我的情况下是点击导航网格)。

如何解决这个问题?

我一直在使用典型的Unity3D代码来获取游戏中的用户输入,例如

if (Input.GetMouseButtonDown(0))
  {

如果我尝试这种方法,结果是一样的

if( Input.touches.Length > 0 )
        {

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

在iOS、Android和桌面上似乎都存在这个问题。

看起来,UI(如UnityEngine.UI.Button)的点击似乎会穿透到游戏中,这似乎是一个基本问题。


1
@JoeBlow 如果在SO上已经有人问过了,那就标记为重复问题,否则这似乎是一个相关主题(一个特定的编程问题,可能对其他人有帮助)。 - Meirion Hughes
3个回答

28

以下是在Unity中的操作步骤:

  1. 自然而然,您的层次结构中将会有一个 EventSystem - 确保您已经有了它。(例如,在添加Canvas时,您会自动获得一个;通常,Unity项目中每个场景都已经有一个 EventSystem,但请确保您已经拥有一个。)

  2. 在相机上添加一个物理射线投射器(只需一次点击)

  3. 执行以下操作:

.

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

基本上,再说一遍,那就是全部内容了。
简单来说:这就是你在Unity中处理触摸的方式。就是这样。
添加一个射线投射器,并加入那段代码。
看起来很容易,而且确实很容易。然而,要做好却可能有些复杂。
(Footnote: 在Unity中执行拖动操作时可能会遇到一些问题:Unity3D中OnPointerDown与OnBeginDrag的问题)
Unity在触摸技术方面的旅程非常引人入胜:
- “早期Unity”……极其简单。毫无用处。根本无法使用。 - “当前‘新’Unity”……运行得很好。非常容易,但在专家级别上使用起来很困难。 - “即将到来的Unity”……到2025年左右,他们将使其同时具备实际功能和易用性。不要抱太高期望。 (情况与Unity的UI系统类似。起初,UI系统令人发笑。现在,它很棒,但在专家级别上使用起来有些复杂。截至2019年,他们将再次对其进行全面更改。)
(网络也是一样。起初它是垃圾。 “新”的网络相当不错,但有一些非常糟糕的选择。最近在2019年,他们又再次更改了网络。)

一个有用的相关提示!

记住!当您有一个全屏的不可见面板,其中包含一些按钮。在全屏不可见面板本身上,您必须关闭射线投射!很容易忘记:

enter image description here


作为一个历史问题:以下是在几年前的Unity中,您曾经可以使用的“忽略UI”的粗略且快速的解决方案...
if (Input.GetMouseButtonDown(0)) { // doesn't really work...
  if (UnityEngine.EventSystems.EventSystem.current.IsPointerOverGameObject())
      return;
  Bingo();
  }

你不能再做这件事了,已经有几年了。


在发布问题之前,我进行了研究,但是没有找到任何有用的信息。显然我使用的关键词不正确。感谢您提供的解决方案,非常有效。 - Peter
@Fattie - 你能否详细描述一下你的解决方案,步骤如何实现?我已经将 physics raycaster 添加到相机中,但是 OnClick 按钮事件仍然会传播到游戏/场景中... - Angel Todorov
天啊,有5bajillion篇文章都说要使用EventSystem.current.IsPointerOverGameObject,但却一无所获,但这个方法完美解决了问题!谢谢! - Min
1
嗨@Min - 对的,Unity存在一个巨大的问题,就是有大量过时的文章。这很棘手。还要注意的是,他们现在正在引入另一个“全新”的UX系统 :/ 真是令人困惑啊。 - Fattie

5

我也遇到了这个问题,但没有找到太多有用的信息,以下是对我有效的方法:

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;
    }
}

基本上,每次点击都会检查点击是否发生在一个UI目标上。

这是一种比较巧妙的方法,可以执行与UI系统自行执行的相同的光线投射,但据我所知,没有更好的方法来让EventSystem告诉你它自己的光线投射是否命中了某个物体。 - Philipp

0
很遗憾,第一次的建议没有帮助我,所以我只是给所有面板标签都设置为“UIPanel”,当鼠标点击时检查是否有任何面板当前处于活动状态。
    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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接