In my code, I’m creating a lot of buttons, I want all of them to have the same onClick function except with a different string.
For that I’m using this piece of code, i insert a string array. it cycles trough every string and creates a button for each string:
( “i” is the string i want to pass)
I think this code should work fine in this case, but it doesn’t, i though i had just written it wrong, but if i try and pass an integer to my buttons using this it works fine. It basically doesn’t work if i pass a string, only with integers.
Is this function only for integers or is there a way to make it work with strings? Is there any other way to do this?
And what’s the signature of ButtonOnClick? In order to pass a string to it, it needs to accept a string argument.
Btw, you can also use the lamdba syntax () => ButtonOnClick(your value here).
With “it doesn’t work” i mean: When the button is pressed it does a Debug.Log(The string i sent), when it does the debug.log it writes an empty message in the console, so it seems like it receives something but only an empty string.
I have tried using the lamdba syntax, it does the same thing.
Of course the results of the lambda will be the same. It was just an alternative, as probably most people find it cleaner these days.
As for receiving the empty string, there’s still some information missing. What does the string array look like? Which values are present? Where do you set ‘i’? Can you post a little more code?
For example, if you were to re-use a variable and only re-assigned in within the for-loop, you’d always see the most-recently assigned value, because it’s not the value that’s captured, but the variable itself (like a reference if you will).
This also caused lots of confusion in older version of C#, because it wasn’t specified where compilers were to declare the iteration variable, which caused one variable to be captured and any sort of closures always saw that variable in its latest state. Meanwhile compilers should take care about the new specification.
This may be an example of the received data, each line will become a string in the string array:
LIST:other_button_09ae_button2_UP1,BOOL,ro
LIST:group5_contact_09ae_in1,BOOL,ro
LIST:group5_contact_09ae_in2,BOOL,ro
LIST:out_relay_09ae_oc1,BOOL,rw
LIST:out_relay_09ae_oc2,BOOL,rw
LIST:out_relay_09ae_exit1,BOOL,rw
LIST:other_contact_09ae_exit1,BOOL,ro
LIST:other_relay_09ae_exit2,BOOL,rw
Which version of Unity are you using? And which scripting runtime version?
Debug.Log the values to the console within the loop
If they show up, add a string variable that’s local to the loop’s scope and assign i to it, then pass this variable instead.
In the most recent version, this should work without using the local variable though.
Are you able to post a minimal example which does not work as expected (a little more than the one that you already posted)? Can’t reproduce what you’re observing. Minimal example being something that can be copy/pasted including the input data without the need to add “missing” pieces.
This is pretty much what I have extracted from my code, the thing is, this code down below works fine in a new unity project, in my other script it doesn’t.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;
public class testingScript: MonoBehaviour {
private string[] commands;
private string responseData;
public GameObject newButtonPrefab; //set this to a prefab of a button
public GameObject Canvas; //set this to your canvas
void Start()
{
responseData = "LIST:out:ther_button_09ae_button2_UP1,BOOL,ro LIST:group5_contact_09ae_in1,BOOL,ro-LIST:group5_contact_09ae_in2,BOOL,ro LIST:out:ut_relay_09ae_oc1,BOOL,rw";
}
public void Example()
{
commands = responseData.Split(' ');
foreach(string i in commands)
{
Debug.Log(i);
GameObject newButton = (GameObject)Instantiate(newButtonPrefab);
newButton.transform.SetParent(Canvas.transform, false);
newButton.transform.localScale = new Vector3(1, 1, 1);
Button tempButton = newButton.GetComponent<Button>();
tempButton.name = i;
tempButton.onClick.AddListener(() => ButtonOnClick(i));
}
}
void ButtonOnClick(string test)
{
Debug.Log(test);
}
}
Already recommended and he said he already tried that and it didn’t work either. Apparently it’s not a problem with capturing the correct variable (see post #8).
@Exarpo Not sure why it would work in one project and in the other it wouldn’t. Have you tried that exact example in both or is it just something you crafted for the new test project?
I read it actually but I wanted to be absolutely sure about this because it is the only thing that can break the code as far as I can tell. Because when the logs during the iteration have the correct value, thats really the only thing which could go wrong.
Because of this logged correct string gets passed into the function, the same log has to show up in the console a second time. It’s just reading the very same unchanged value twice, it can’t be anything else, right?
Yes, it’s probably better to be checked twice. I also can’t make any sense of it so far but speaking from experience there’s always the that one hidden detail that noone paid attention to.
I’m really looking forward to seeing what it is in this case. Perhaps it’s really just that and my explanation was just not clear enough. But the version he’s using should close its captures correctly over separate iteration variables and his latest example seems to work flawlessly in a new project.
I just realized that the data that it receives usually has “special” characters such as ‘á’. Could that somehow break the passing of the string? Because when I tried it in the other project i didn’t include any of these characters.
And “Have you tried that exact example in both or is it just something you crafted for the new test project?” (post #14):
For the other project I created a replica of the script that only included the important parts.
Special characters shouldn’t matter - just add + " " + test.Length to your second debug log to check.
Also, could you provide a screenshot of what is happening in the console as well as the full script maybe?
It’s just impossible for a local value to vanish after time, when you use it in a lambda … somehow the string value has to be cleared right before passing it into the lambda or it is empty in the first place, or the log does not show up at all - but having two logs in the console, one showing the value and another one, not showing the same value just cannot happen.
When i add “+ test.Length” it prints a zero, meaning the string really is empty.
This is the only piece of the code that should matter in this situation, iut’s where I create the buttons. I log “i” just before passing it, and in that log it is exactly as it should be.
foreach(string i in commands)
{
GameObject newButton = (GameObject)Instantiate(newButtonPrefab);
newButton.transform.SetParent(buttonsContentPanel.transform, false);
newButton.transform.localScale = new Vector3(1, 1, 1);
Button tempButton = newButton.GetComponent<Button>();
tempButton.name = i;
Debug.Log(i);
tempButton.onClick.AddListener(() => ButtonOnClick(i));
}
I know its not the solution why this wont work.
But if you are setting the button name to i than you could pass the button itself instead i to ButtonOnClick and access the name in the function.
When you have more complicated use cases i would write a own component MyButton with a command variable instead of using the name and pass the component type to the function.