Trouble With Canvas Buttons

I have a Button array. I am trying to check if a Button in the array is clicked/pressed and then execute code (play a sound effect) on that frame.

I tried 4 different ways of using Button.onClick.AddListener(), but they all resulted in the last Button being effectively pressed. In the Buttons() function in my code below, I commented out each of my 4 attempts and explained how they cause only the last button effect to occur regardless of which button is pressed. There is also an issue with code running hundreds of times when it shouldn’t run at all. It seems to me that Button.onClick.AddListener() runs at the end of the frame no matter where it is written in the code, and it runs hundreds of times per click.

Could someone please show me what I’m doing wrong here?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ButtonSFXScript : MonoBehaviour
{

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public AudioClip[] sfx_AudioClipArray; // Set in editor
    public Transform buttonSlider_Transform; // Set in editor

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public int setIndex_int = -1;
    public bool setPlayIndex_bool;
    public bool limitSet_bool;

    public Button[] clickable_ButtonArray;
    public Button current_Button;
    public int buttonCount_int;

    public AudioSource this_audioSource;

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    void Start()
    {
        this_audioSource = GetComponent<AudioSource>();

        buttonCount_int = buttonSlider_Transform.childCount;

        clickable_ButtonArray = new Button[buttonCount_int];
      
        for (int i_int = 0; i_int < clickable_ButtonArray.Length; i_int++)
        {
            clickable_ButtonArray[i_int] = buttonSlider_Transform.GetChild(i_int).GetComponent<Button>();
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    private void Update()
    {
        Buttons();
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    private void Buttons()
    {
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /*
        ///Attempt 0:

        limitSet_bool = false;
      
        for (int i_int = 0; i_int < clickable_ButtonArray.Length; i_int++)
        {
            if (limitSet_bool)
            {
                i_int = clickable_ButtonArray.Length;

                Debug.Log("Breaking from for loop...");
            }
            else
            {
                setIndex_int = i_int;
              
                clickable_ButtonArray[setIndex_int].onClick.AddListener(delegate { PlaySFXNow(setIndex_int); });
            }
        }

        ///Problem: No Matter which Button is clicked, the last clip in sfx_AudioClipArray is played.
            Also the message "Why is this code executing? [PlaySFXNow]" is logged hundres of times.

        */

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /*
        ///Attempt 1:
      
        limitSet_bool = false;

        for (int i_int = 0; i_int < clickable_ButtonArray.Length; i_int++)
        {
            if (limitSet_bool)
            {
                i_int = clickable_ButtonArray.Length;

                Debug.Log("Breaking from for loop...");
            }
            else
            {
                setIndex_int = i_int;
              
                current_Button = clickable_ButtonArray[setIndex_int];

                current_Button.onClick.AddListener(delegate { PlaySFXNow(setIndex_int); });
            }
        }

        ///Problem: Same as Attempt 0: No Matter which Button is clicked, the last clip in sfx_AudioClipArray is played.
            Also the message "Why is this code executing? [PlaySFXNow]" is logged hundres of times.

        */

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /*
        ///Attempt 2:

      
        limitSet_bool = false;

        for (int i_int = 0; i_int < clickable_ButtonArray.Length; i_int++)
        {
            if (!setPlayIndex_bool)
            {
                setIndex_int = i_int;

                current_Button = clickable_ButtonArray[setIndex_int];

                current_Button.onClick.AddListener(delegate { SetPlayIndex(setIndex_int); });
            }
            else
            {
                i_int = clickable_ButtonArray.Length;

                Debug.Log("Breaking from for loop...");
            }
        }

        if (setPlayIndex_bool)
        {
            PlaySFX(setIndex_int);
        }
      
        ///Problem: Same as Attempt 0: No Matter which Button is clicked, the last clip in sfx_AudioClipArray is played.
            Also the message "Why is this code executing [SetPlayIndex]" is logged hundres of times.

        */



        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        /*
        ///Attempt 3:

        limitSet_bool = false;

        for (int i_int = 0; i_int < clickable_ButtonArray.Length; i_int++)
        {
            if (!setPlayIndex_bool)
            {
                setIndex_int = i_int;

                current_Button = clickable_ButtonArray[setIndex_int];

                current_Button.onClick.AddListener(SetPlayIndex);
            }
            else
            {
                i_int = clickable_ButtonArray.Length;

                Debug.Log("Breaking from for loop...");
            }
        }

        if (setPlayIndex_bool)
        {
            PlaySFX(setIndex_int);
        }        
      
        ///Problem: Same as Attempt 0: No Matter which Button is clicked, the last clip in sfx_AudioClipArray is played.
            Also the message "Why is this code executing [SetPlayIndex]" is logged hundres of times.

        */

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public void PlaySFXNow(int clipIndex_int)
    {
        if (!limitSet_bool)
        {
            limitSet_bool = true;

            Debug.Log("clipIndex_int: " + clipIndex_int);

            if (clipIndex_int < sfx_AudioClipArray.Length)
            {
                this_audioSource.clip = sfx_AudioClipArray[clipIndex_int];

                this_audioSource.Play();

                Debug.Log("Play: " + this_audioSource.clip.name);
            }
        }
        else
        {
            Debug.Log("Why is this code executing? [PlaySFXNow]");
        }
    }

    public void PlaySFX(int audioClipIndex_int)
    {
        if (!limitSet_bool)
        {
            setPlayIndex_bool = false;

            PlaySFXNow(audioClipIndex_int);
        }
        else
        {
            Debug.Log("Why is this code executing? [PlaySFX]");
        }
    }
  
    public void PlaySFX()
    {
        PlaySFX(setIndex_int);
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    public void SetPlayIndex(int index_int)
    {
        if (!setPlayIndex_bool)
        {
            Debug.Log("setPlayIndex_bool set to true when setIndex_int: " + setIndex_int);

            setPlayIndex_bool = true;
        }
        else
        {
            Debug.Log("Why is this code executing [SetPlayIndex]?");
        }
    }

    public void SetPlayIndex()
    {
        SetPlayIndex(setIndex_int);
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

Main issue is that the “onClick” event of the buttons do not store any parameters when you addlistener like that. What you can do to “store” a base type parameter in an event is to use lambdas.

within your forloop where you add listener:

current_Button = clickable_ButtonArray[setIndex_int];
int tempIndex = setIndex_int;//this is crucial, to have the value stay as it is before applying it.
current_Button.onClick.AddListener(()=>{ PlaySFXNow(tempIndex); });

It might actually have worked with what you attempted with the delegate if you included the crucial step.

1 Like

Everything that @Laparen points out above… this is known as variable capture versus value capture, and here’s more reading / examples:

Delegate / Action variable capture / value capture:

Thank you Laperen and Kurt-Dekker. That change made the difference; it works now.

1 Like