- Home /
Runtime UI button creation: can't make an "OnClick" where I call a function from another script while sending a parameter.
I've looked around and even though there were people with similar problems to this one, none of the solutions could help my case.
What I want to do is to create multiple buttons at runtime, all of the buttons will call the same function on another script, but will provide different parameters (gameObjects).
So this is the code:
for (int i = 0; i < cockpits.Count; i++)
{
//Create Buttons
GameObject b = GameObject.Instantiate (buttonPrefab);
b.transform.SetParent (cockPitsRect.transform, false);
b.transform.localScale = new Vector3 (1, 1, 1);
Text t = b.GetComponentInChildren<Text> ();
t.text = cockpits [i].name;
Button tempButton = b.GetComponent<Button> ();
tempButton.onClick.AddListener(() => shipScript.AddPart(cockpits[i]));
RectTransform buttonRect = tempButton.GetComponent<RectTransform> ();
b.name = cockpits [i].name;
buttonRect.anchoredPosition = new Vector2 (0, 0.5f);
buttonRect.localPosition = new Vector2(cockPitsRect.sizeDelta.x /2 *-1 + buttonRect.sizeDelta.x/2 + i*100,0);
cockpitButtons.Add (b);
}//For
And this is the part that is causing trouble:
tempButton.onClick.AddListener(() => shipScript.AddPart(cockpits[i]));
I mean, it should be simple, right? I call the function "AddPart" on the "shipScript" script, and provide the parameter which is the gameobject [i] on the list of gameobjects "cockpits".
But it doesn't work. The game compiles just fine, but when I try to click on any of the 3 buttons, this appears on the console:
ArgumentOutOfRangeException: Argument is out of range
So I tried debbuging it and turns out that the "i" value, even though it should be 0, 1, and 2 for the first, second and third buttons respectively, becomes "3" when I press the button (that is why it becomes out of range I guess?).
So I really have no idea how can I make this work.
Thanks in advance! :)
Answer by M-Hanssen · May 09, 2016 at 08:37 AM
Try setting the cockpit variable to a local variable before inserting it into the lambda expression like this:
//(I don't know the exact type of the cockpits array value, so change if necessary)
Cockpit cockpit = cockpits[i];
tempButton.onClick.AddListener(() => shipScript.AddPart(cockpit));
This is it. It worked! Unity answers is awesome. Thank you a lot sir!
Answer by Ryanless · May 09, 2016 at 09:27 AM
yep, i had the same problem once. This is how i solved it. Pretty similar to Hanssen, though i gess making a local int is slighlty less cpu demanding than making new local cockpit variables ;-).
int t = i;
tempButton.onClick.AddListener(() => shipScript.AddPart(cockpits[t]));
I tested and this also works! Thank you for the help! I spent hours stuck with this problem.