Unity按钮单击不触发事件

时间:2018-05-28 22:08:45

标签: c# unity3d unity-ui

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

我在面板中创建了一个带有多个按钮的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()并“单击!”正确输出到控制台。

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

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

1 个答案:

答案 0 :(得分:2)

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

  • 确保相机和按钮之间没有ui元素(例如透明面板或图像);您可以在运行时移动对象,看它们是否太近并且消耗了点击。
  • 确保父画布没有CanvasGroupInteractable设置为false。
  • 查找可能阻挡光线投射的任何空对象/碰撞器/侦听器。
祝你好运!

修改

重新阅读您的代码后,再次给予链接的帖子我已经意识到您的代码中存在错误。

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

带上我的建议!解决问题的方法有很多种,所以最终选择最适合自己的方式。