4

我查看了此处处理相同问题的帖子,但它既没有说明问题所在,也没有解决我的问题。

我在面板中创建了一个带有多个按钮的 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。

4

1 回答 1

5

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

  • 确保相机和按钮之间没有ui元素(例如透明面板或图像);您可以在运行时移动对象以查看它们是否太靠近并消耗点击。
  • 确保父画布没有CanvasGroup设置Interactable为 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();
    }
}

几点注意事项

  • 即使您从未使用过该类的任何成员,您也应该使用Canvasitem 而不是;它使以后在路上更容易阅读,并防止您意外构建当您尝试访问孩子时不会做任何事情的构造。该对象继承自,因此您将拥有所需的一切。GameObjectCanvasnew GameOptions(someRandomButton)CanvasGameObject
  • 您应该考虑使用不同的命名方案;这是非常主观的意见,如果你搜索 SO,关于这个主题会有很多争论。就个人而言,我选择私有字段的下划线前缀,例如private GameObject _canvas;,这让我零怀疑我没有忘记 athis因为参数和字段本质上将具有不同且唯一的命名方案。

接受我的建议!有很多方法可以解决问题,所以最终选择最适合您的方法。

于 2018-05-28T22:38:27.863 回答