- Home /
Add Listeners to array of Buttons
Hi, using unity 5.6 -
I have three buttons that I am attempting to instantiate with listeners in C#. In my function TaskOnClick(), I want to be able to be able to tell which button has been clicked so I can add further functionality.
I am fairly sure I am mucking up some code in the Start() function and also not sure if I need to add a bool to the TaskOnClick() function or if I should just use IF or Switch statements to parse through the clicks. I have been following this example: https://docs.unity3d.com/ScriptReference/UI.Button-onClick.html
Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Button02 : MonoBehaviour {
public Button[] buttons;
private void Start()
{
for (int i = 0; i < buttons.Length; i++)
{
Button btns = buttons[i].GetComponent<Button>();
btns.onClick.AddListener(TaskOnClick);
}
}
public void TaskOnClick()
{
// how can I add a boolean here to check which button has been clicked?
Debug.Log("you have clicked this button");
}
}
I am wondering if something along the lines of this in TaskOnClick() might work: (obviously this is very crude....) Thank you in advance.
public void TaskOnClick() {
for (int i= 0; i < Lenght; i++) {
if (buttons[0] == true)
'do something'....
else if(buttons[1] == true)
'do something else'
Answer by Hellium · Jul 10, 2017 at 01:10 PM
All the answers given previously won't work because of the closure problem.
While the idea of using a specific delegate it the way to go, you have to "capture" the value of the loop index before assigning the delegate :
for (int i = 0; i < buttons.Length; i++)
{
int closureIndex = i ; // Prevents the closure problem
buttons[closureIndex].onClick.AddListener( () => TaskOnClick( closureIndex ) );
}
// ...
public void TaskOnClick( int buttonIndex )
{
Debug.Log("You have clicked the button #" + buttonIndex, buttons[buttonIndex]);
}
I was unaware of the closure problem. The solution appears to do logically-speaking the same thing, but you are right. C# understands your code differently than $$anonymous$$e. Thanks for the info.
@Hellium and @CoryButler -
I appreciate your responses. It has solved the issue of adding a listener. Thank you.
thank you i spent over 6 hours trying to figure out why my code wouldnt work. weirdly enough i have never heard of this closure problem, could you please explain why adding a new int param inside a for loop and assigning it the iterators value works. essentially using i in the for loop should do the exact same thing?
I may not be the best one to explain the problem, but I will try.
for (int i = 0; i < buttons.Length; i++)
{
buttons[closureIndex].onClick.AddListener( () => TaskOnClick( i ) );
}
With the previous code, you add a new listener to the onClick
event. This listener in the following UnityAction
: () => TaskOnClick( i )
. When one of the button is clicked, TaskOnClick
is called, but it's not the value of i
at the i
th iteration which is used as the argument. It's a "reference" of i
. Since there is 99,99% chance the for
loop has ended beforehand, i
contains the value buttons.Length
.
By creating a new variable inside the loop, when TaskOnClick( closureIndex )
is called, the reference of the temporary variable is used as the parameter, and the problem disappears.
I hope it's clear enough for you.
Answer by CoryButler · Jul 10, 2017 at 12:36 PM
Use a delegate to pass the button's ID as an argument. Also, skip the btns
variable.
for (int i = 0; i < buttons.Length; i++)
{
buttons[i].GetComponent<Button>().onClick.AddListener(delegate{TaskOnClick(i);});
}
Then, change the TaskOnClick()
signature to TaskOnClick(int id)
.