Hello there,
well my title isn’t quit explicit but here’s my issue.
I’m having an inventory system where I need to instantiate a button for every item (in that case, readable notes).
Here’s the function that update the panel containing notes
private void UpdateNotePanel()
{
GameObject noteButtonPrefab = Resources.Load<GameObject>("Prefabs/NoteButton");
if(noteButtonPrefab == null)
{
Debug.LogError("Button prefab missing");
return;
}
foreach(Note n in player.Inventory.GetAllNotes())
{
//If the button isn't already present in the notelayout
if(noteLayout.transform.FindChild(n.textFileName + "_but") == null)
{
GameObject instance = Instantiate<GameObject>(noteButtonPrefab);
instance.name = n.textFileName + "_but";
instance.transform.SetParent(noteLayout.transform);
instance.transform.localScale = new Vector3(1, 1, 1);
instance.GetComponentInChildren<Button>().GetComponentInChildren<Text>().text = n.textFileName;
Debug.Log("setting button " + n.textFileName + "_but ");
//This line is acting weird.
instance.GetComponentInChildren<Button>().onClick.AddListener(() => { NoteReader.noteReader.Read(n, 1); });
}
}
}
for a reason I don’t understand the parameters I pass to noteReader.Read() are, for all button I instantiate, the last one in the foreach loop.
(I’m not quit sure everybody understand what I mean but that’s kind of tricky to explain…)
Instead of passing the current Note for each current instantiated button, it passes the last one to all of them.
What is going on here ? I am missing something ?
Thanks in advance.
Lamda expressions such as the ones used to add listeners to UI buttons have strange behavior when put into loops like this. I’m not sure why it would just work by changing foreach to a for loop, as I had thought the problem was something else.
I know from my own experience that when using Lamdas in for-loops, you have to save a copy of the indexer to a local variable in the loop in order for it to work right. So for example:
for(int i = 0; i < Notes.Length; ++i)
{
//Assuming that returns an array (if it's a list, then .Count)
int num = i;
if(noteLayout.transfomr.FindChild(Notes[i] + "_but") == null)
{
GameObject instance = Instantiate<GameObject>(noteButtonPrefab);
instance.name = Notes[i].textFileName + "_but"; //Using "i" anywhere else is fine, just not on the Lambda
instance.transform.SetParent(noteLayout.transform);
instance.transform.localScale = new Vector3(1, 1, 1);
instance.GetComponentInChildren<Button>().GetComponentInChildren<Text>().text = Notes[i].textFileName;
Debug.Log("setting button " + Notes[i].textFileName + "_but ");
instance.GetComponentInChildren<Button>().onClick.AddListener(() => { NoteReader.noteReader.Read(Notes[num], 1); }); //Passing the local variable instead of the indexer works, using "i" will always return the last one
}
}
It’s a little more difficult since I don’t know what all your references do or what type GetAllNotes returns, but the issue is pretty much the same.
Well, saving a reference of the current value with the indexer to a local variable seems to work aswell as it is what I did.
But indeed, that’s kind of weird.