Dynamically Adding Button OnClick Not Working

So I have a prefab for an alert dialog which I can instantiate when it’s needed and set the text of the dialog and buttons to whatever the context calls for.

Right now I want to use it as an end-of-game popup that just lists your score and allows you to start a new game by clicking one of the buttons.

The dialog is instantiating fine but I can’t get the dynamically added onClick event to work as I expect.

Here’s the prefab itself and then the button properties:

Here’s the simple code I’m using to instantiate the dialog and assign the onClick:

private void EndGame()
    {
        // setup end game dialog
        Button affirmativeButton = AlertDialogPrefab.transform.GetChild(1).GetChild(0).GetComponent<Button>();
        Text dialogText = AlertDialogPrefab.transform.GetChild(2).GetChild(0).GetComponent<Text>();
        Text affirmativeButtonText = affirmativeButton.transform.GetChild(0).GetComponent<Text>();
        Text negativeButtonText = AlertDialogPrefab.transform.GetChild(1).GetChild(1).GetChild(0).GetComponent<Text>();
        Text headerText = AlertDialogPrefab.transform.GetChild(3).GetChild(0).GetComponent<Text>();

        headerText.text = "Game Over";
        dialogText.text = string.Format("Your score: {0}{1}High score:{2}{1}{1}Try another round?", CurrentPoints, Environment.NewLine, "none");
        affirmativeButtonText.text = "Ok";
        negativeButtonText.text = "Back";

        affirmativeButton.onClick.AddListener(StartNewGame);

        Instantiate(AlertDialogPrefab, InGameUiCanvas);
    }

    private void StartNewGame()
    {
        Debug.Log("Start new game");
    }

The “Start new game” log is never happening. I put a breakpoint on the “affirmativeButton.onClick.AddListener(StartNewGame);” line and when I first instantiate the dialog, it hits the breakpoint, as expected, and the dialog appears. When I click the affirmative button, that breakpoint is hit AGAIN and another dialog is created, like the game is calling that “EndGame” method again, for some reason, and it’s not calling that “StartNewGame” method that I’m assigning.

Here’s the two dialogs after I’ve clicked the affirmative button:

I have no idea what’s going on with this; it doesn’t make sense.

You’re making changes to the prefab instead of to the instance you are creating from the prefab. Think of the prefab as a blueprint for a house, and the instance as the actual house you are building. Not only are you making changes to the blueprint, but those changes will persist in the editor after you stop play mode too!

The answer is to do the Instantiate first, then make your changes to the resulting instance:

private void EndGame() {
    GameObject instance = Instantiate(AlertDialogPrefab, InGameUiCanvas);

    // setup end game dialog
    Button affirmativeButton = instance.transform.GetChild(1).GetChild(0).GetComponent<Button>();
    Text dialogText = instance.transform.GetChild(2).GetChild(0).GetComponent<Text>();
    Text affirmativeButtonText = affirmativeButton.transform.GetChild(0).GetComponent<Text>();
    Text negativeButtonText = instance.transform.GetChild(1).GetChild(1).GetChild(0).GetComponent<Text>();
    Text headerText = instance.transform.GetChild(3).GetChild(0).GetComponent<Text>();

    headerText.text = "Game Over";
    dialogText.text = string.Format("Your score: {0}{1}High score:{2}{1}{1}Try another round?", CurrentPoints, Environment.NewLine, "none");
    affirmativeButtonText.text = "Ok";
    negativeButtonText.text = "Back";

    affirmativeButton.onClick.AddListener(StartNewGame);
}
2 Likes

Thanks a lot for your reply!

That does make sense, and I tried exactly what you have, and it’s acting the same still.

Maybe there’s some other little thing I didn’t do or something?