Dynamically create button listeners with parameter passing.

I want to have a create a pop-up menu which lists the player’s inventory (as buttons) and when one is selected, that item is spawned.

To create the buttons I iterate over the player’s inventory but I’m not sure how to add a listener to each button so that the correct item is passed to the spawner class as a parameter?

I am also open to alternative methods of achieving this goal.

private void CreateButtons()
    {
        presentedButtons = new List<UnityEngine.UI.Button>();

        for (int i = 0; i < inventoryToDisplay.Container.ItemLines.Count; i++)
        {
            presentedButtons.Add(Instantiate(ButtonPrefab, new Vector3(XStartPosition, YStartPosition - (i * YSpaceBetweenRows), 0), Quaternion.identity));
            presentedButtons[i].transform.SetParent(GetComponent<RectTransform>(), false);
            TMP_Text txt = presentedButtons[i].GetComponentInChildren<TMP_Text>();
            txt.text = inventoryToDisplay.Container.ItemLines[i].item.Name + " (" + inventoryToDisplay.Container.ItemLines[i].amount + ")";

            presentedButtons[i].onClick.AddListener(() => {
                _spawner.testButtonPress(inventoryToDisplay.Container.ItemLines[i].item);
            });
        }
    }

You’re almost there… the above is likely perfect EXCEPT that C# does “variable capture” instead of value capture.

This means that your listeners will ALL be created and use the final value of i, not an ever-increasing 0, 1, 2, 3 etc which is what you want.

SO… what you want to do is make a local variable inside the for() loop block:

int i2 = i;   // capture for delegate

And now use i2 anywhere in the delegate. Each time through the loop that is a brand-new instance of the local variable, each one containing 0, 1, 2, 3, 4, etc. and “captured” individually by the C# delegate.

6 Likes

This solved the problem. Thank you.

1 Like