Creating several UI buttons with onclick events

Hello, I am instantiating several buttons which I want to be connected to an index of sorts. This is because I want to be able to determine which button was pressed.
However when I press any of the buttons they all output the very last index.
For example if I have 10 buttons and I press button #3, it should output 2 but instead it gives me 9.

EDIT: Just as I posted this I was able to fix it by simply adding int index = i; at the start of the for loop and then using that variable in my SelectTile method. Not sure what is going on here, but it works.

    void LoadTiles()
    {
        Button btn;
        Image img;

        for (int i = 0; i < Tiles.Length; i++)
        {
            btn = Instantiate(tileButtonPrefab, content).GetComponent<Button>();
            btn.onClick.AddListener(() => SelectTile(i));
            img = btn.GetComponent<Image>();
            img.sprite = Tiles[i].sprite;
        }
    }

    public void SelectTile(int index)
    {
        selectedTileIndex = index;
    }

It’s called a closure. Your lambda captures and holds the variable i, until the lambda is called, only then is the lambda actually defined. So it is defined after your loop is finished, when i is 9.

1 Like

I think I understand. So when I create a new variable every iteration of the loop, those variables wont change after they have been defined. So when the lambda is called it will use the correct number. Thanks a lot for this explanation!

Yeah, you changed it so that it captures references to variables that aren’t being changed.

Consider this code.

Anonymous functions can modify local variables, so those variables are reference types.

public class Anonymous : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        int x = 0;

        //Anonymous Function captures reference to local variable x
        Action printX = () =>
        {
            //increment local variable
            x++;
            Debug.Log($"Anonymous Function: {x}");
        };

        //outputs 1
        printX();

        //Delayed execution
        //outputs 3 because it runs last
        StartCoroutine(DelayedExecution(2f, printX));

        //outputs 2 but runs before delayed execution
        printX();
    }

    public IEnumerator DelayedExecution(float seconds, Action method)
    {
        yield return new WaitForSeconds(seconds);
        Debug.Log("Delayed Execution");
        method();
    }
}
1 Like