统一按钮点击不触发事件

Unity button click not firing event

我查看了处理相同问题的 ,但它既没有说明错误也没有解决我的问题。

我创建了一个 Canvas 预制件,面板中有许多按钮。我在运行时实例化预制件并获取面板中的所有 Button 对象。然后我为所有调用相同 clicked() 方法

的按钮添加一个监听器到 onClick() 事件
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!" 会正确输出到控制台。

但是,none 个按钮在单击时触发事件。我还注意到检查器中 OnClick 中的 PersistentCalls.Calls 数组在运行时对所有按钮都有 0 个元素。

我在 Windows 10 64 中使用 Unity 2017.4.3f1。

您没有抛出任何异常,并且 onClick.Invoke() 正在触发,这表明问题在于其他一些元素正在消耗点击次数。没有你的项目在我面前,我只能提一些建议。

  • 确保相机和按钮之间没有ui元素(例如透明面板或图像);您可以在 运行 时间四处移动对象,看看它们是否靠得太近并消耗点击次数。
  • 确保父 canvas 没有将 Interactable 设置为 false 的 CanvasGroup
  • 寻找任何可能阻挡光线投射的空objects/colliders/listeners。

祝你好运!


编辑

在重新阅读您的代码并再次查看链接 post 之后,我发现您的代码中存在一个错误。

在您的 GameOptions class 构造函数中,您在收集对象时实际上并未引用实例化对象。你写了这个:

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

如果您查看到底发生了什么,您会将字段 canvas 分配给传递给构造函数参数的预制件。进行赋值后,您使用参数实例化预制件,而不引用实例化对象。

之后,您将在 prefab 上调用 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 项目而不是 GameObject,即使您从不使用 Canvas class 的任何成员;它使以后阅读起来更容易,并防止您意外构建 new GameOptions(someRandomButton) 当您尝试访问子项时它不会做任何事情。 Canvas 对象继承自 GameObject,因此您将拥有所需的一切。
  • 您应该考虑使用不同的命名方案;这是非常主观的意见,如果你搜索 SO,就会有很多关于这个主题的争论。就我个人而言,我选择在私有字段上使用下划线前缀,例如 private GameObject _canvas;,这让我毫不怀疑我没有忘记 this,因为参数和字段本来就具有不同且唯一的命名方案。

请对我的建议持保留态度!解决问题的方法有很多种,所以最终选择最适合您的方法。