统一按钮点击不触发事件
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
,因为参数和字段本来就具有不同且唯一的命名方案。
请对我的建议持保留态度!解决问题的方法有很多种,所以最终选择最适合您的方法。
我查看了处理相同问题的
我创建了一个 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
,因为参数和字段本来就具有不同且唯一的命名方案。
请对我的建议持保留态度!解决问题的方法有很多种,所以最终选择最适合您的方法。