Unity按钮点击无法触发事件。

5
我查看了这里的帖子,它涉及相同的问题,但既没有说明问题出在哪里,也没有解决我的问题。
我创建了一个Canvas预制件,在面板内有许多按钮。我在运行时实例化预制件,并获取面板内所有Button对象。然后,我为所有按钮添加了一个onClick()事件监听器,调用相同的clicked()方法。
public class GameOptions
{
    private GameObject canvas;

    public GameOptions(GameObject canvas)
    {
        this.canvas = canvas;
        GameObject.Instantiate(canvas);        
        Text[] textObjects = canvas.GetComponentsInChildren<Text>();
        Button[] buttonObjects = canvas.GetComponentsInChildren<Button>();

        for (int i = 0; i < buttonObjects.Length; i++)
        {
            Debug.Log(buttonObjects[i].name);
            buttonObjects[i].onClick.AddListener(() => clicked());
            buttonObjects[i].onClick.Invoke();
        }
    }

    public void clicked()
    {
        Debug.Log("Clicked!");
    }
}

请注意,当我通过代码调用事件时,clicked()会被调用,并且"Clicked!"正确地输出到控制台。
但是,当单击任何按钮时,都没有触发事件。我还注意到,在运行时,OnClick中的PersistentCalls.Calls数组对于所有按钮都具有0个元素。
我正在使用Windows 10 64位下的Unity 2017.4.3f1。
2个回答

5
您没有抛出任何异常,而且 onClick.Invoke() 被触发表明问题在于其他元素正在消耗点击事件。由于我没有您的项目,所以只能提出一些建议。
  • 确保摄像机和按钮之间没有 UI 元素(如透明面板或图像);您可以在运行时移动对象以查看它们是否过于接近并消耗了点击事件。
  • 确保父级画布没有设置 Interactable 为 false 的 CanvasGroup
  • 查找可能阻止光线投射的任何空对象/碰撞器/监听器。

祝好运!


编辑

重新阅读您的代码并再次浏览链接的帖子后,我发现了您代码中的一个错误。

在您的 GameOptions 类构造函数中,在收集对象时您实际上没有引用已实例化的对象。您编写了以下内容:

this.canvas = canvas;
GameObject.Instantiate(canvas);
Text[] textObjects = canvas.GetComponentsInChildren<Text>();

如果你仔细看一下发生了什么,你会发现你正在将字段canvas分配给传入构造函数参数的预制件。分配完成后,你使用参数实例化预制件,而没有任何参考已实例化的对象。
之后,你正在调用GetComponentsInChildren在一个预制件上而不是实例化的对象本身。这就是为什么onClick.Invoke()被触发的原因,因为对象存在于预制件中;它们只是不是你要找的对象。
我已经重构了你的构造函数,这应该解决你的问题。
public GameOptions(GameObject canvas)
{
    //here we instantiate the canvas item, assigning it to the field
    this.canvas = GameObject.Instantiate(canvas);  

    //then we reference the field item, instead of the parameter item
    Text[] textObjects = this.canvas.GetComponentsInChildren<Text>();
    Button[] buttonObjects = this.canvas.GetComponentsInChildren<Button>();
    for(int i = 0; i < buttonObjects.Length; i++)
    {
        Debug.Log(buttonObjects[i].name);
        buttonObjects[i].onClick.AddListener(() => clicked());
        buttonObjects[i].onClick.Invoke();
    }
}

几条注记

  • 即使您从未使用Canvas类的任何成员,您也应该使用Canvas项而不是GameObject,因为这样可以使后期读取更加容易,并且防止在尝试访问其子项时无意中构造new GameOptions(someRandomButton)将不起作用。 Canvas对象继承自GameObject,因此您将拥有所需的一切。
  • 您应该考虑使用不同的命名方案;这是高度主观的意见,在搜索SO时存在很多争议。个人而言,我选择使用下划线前缀来表示私有字段,例如private GameObject _canvas;,这使我毫不怀疑自己没有忘记this,因为参数和字段本质上具有不同和唯一的命名方案。

请谨慎采纳我的建议!解决问题有很多方法,因此最终选择最适合您的方法。


很好的建议,但我认为还有其他事情发生了。在我的原始帖子中,您可以看到当我在运行时调试模式下检查检查器时,我可以看到没有任何事件与任何按钮的OnClick相关联。 - Darren Cook
@DarrenCook 噢,对不起,我可能是匆匆看过了。我马上会更新我的答案,提出另一个建议,我认为它可能会解决你的问题!如果行不通,请告诉我! - Matthew Andrews
太好了 - 非常感谢。这是我犯的一个愚蠢的疏忽。现在已经修复了。 - Darren Cook

0

为了帮助更多遇到这个烦人问题的人。

除了常规的 EventSystemCanvas 配置之外,我在自己的程序中发现了另一个导致 UI 按钮无响应的原因,我认为这可能是大多数人的原因。

当您自己添加一个 camera(非常常见)而没有标记它时,就会出现此问题。如果您不知道如何标记它,则很可能是您的原因。

关键点:您使用的与 standalone input module 一起使用的 camera 必须标记为 MainCamera。否则,它将无法识别没有碰撞器的对象。


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