One upgrade button is holding various upgrades

I’m working on a Vampire Survivors like game, and i’m having issues on the upgrade selection.
The screen and the upgrade actually are working fine. But my consecutives upgrades are multiplicated. For example, when i level up, the upgrade screen appears, the first button is the “attack speed” button, i click, get the bonus, great! Level up again, now the first button are the “Damage” button, i click and i get both the attack speed and damage bonuses, on the third time, i get both attack speed, damage and whatever bonus i pick up, and so on. I already searched a lot on the internet, but found nothing. Anyone can help?
(English is not my primary language, so i’m sorry if i misspelled something)

Here is the code:

Upgrade[] Upgrades;
public int upgradeCount;
private bool flag;

[SerializeField] GameObject Upgrade_prefab;
[SerializeField] GameObject Upgrade_panel;
[SerializeField] GameObject Inventory;

void Start()
{

    Upgrades = new Upgrade[]
{
    new Upgrade { Name = "Multiple shot", Description = "Increase your shot by 1 time", Rarity = 10, Increase = 1, Level = 0, sprite = Resources.Load<Sprite>("PowerUps/Multiple Shot")},
    new Upgrade { Name = "Attack Speed", Description = "Increase your fire rate by 10%", Rarity = 10, Increase = 10, Level = 0, sprite = Resources.Load<Sprite>("PowerUps/Attack Speed")},
    new Upgrade { Name = "Armour", Description = "Increase your defense by 10%", Rarity = 10, Increase = 10, Level = 0, sprite = Resources.Load<Sprite>("PowerUps/Armour")},
    new Upgrade { Name = "Damage", Description = "Increase your damage by 10%", Rarity = 10, Increase = 10, Level = 0, sprite = Resources.Load<Sprite>("PowerUps/Damage")},
    new Upgrade { Name = "Piercing Shot", Description = "Your shot will pass more 1 enemy", Rarity = 10, Increase = 10, Level = 0, sprite = Resources.Load<Sprite>("PowerUps/Piercing Shot")},
    new Upgrade { Name = "Bouncing Shot", Description = "Your shot will ricochet on the walls 1 more time", Rarity = 10, Increase = 10, Level = 0, sprite = Resources.Load<Sprite>("PowerUps/Bouncing shot")},

};
    Upgrade_panel.SetActive(false);
    Debug.Log(Upgrades[1].Level + Upgrades[1].Name);
}
void Update()
{

    if (Input.GetKeyUp(KeyCode.L)) ButtonsSet();
}

public class Upgrade
{
    public string Name;
    public string Description;
    public int Rarity;
    public float Increase;

    public int Level;
    public Sprite sprite;

}
private void ButtonsSet()
{
    Upgrade_panel.SetActive(true);
  

    List<int> AvaliableUpgrades = new List<int>();
    for (int i = 0; i < Upgrades.Length; i++)
    {
        AvaliableUpgrades.Add(i);

    }
    ShuffleList(AvaliableUpgrades);

    while(Upgrade_panel.transform.childCount < upgradeCount)
    {
        Instantiate(Upgrade_prefab, Upgrade_panel.transform);
    }
    
    for (int i = 0; i < upgradeCount; i++)
    {
        
        Upgrade upgrade = Upgrades[AvaliableUpgrades[i]];

        GameObject upgradeObject = Upgrade_panel.transform.GetChild(i).gameObject;
        Button upgradeButton = upgradeObject.GetComponent<Button>();

        

        Text upgradeTextName = upgradeButton.transform.GetChild(0).GetComponent<Text>();
        upgradeTextName.text = upgrade.Name;

        Text upgradeTextDescription = upgradeButton.transform.GetChild(1).GetComponent<Text>();
        upgradeTextDescription.text = upgrade.Description;

        upgradeObject.transform.GetChild(2).GetComponent<Image>().sprite = upgrade.sprite;
        upgradeButton.onClick.AddListener(() => { UpgradeChosen(upgrade.Name,upgrade.sprite, upgrade); });


    }
    Time.timeScale = 0;

}

private void UpgradeChosen(string upgradeChosen, Sprite sprite, Upgrade SelectedUpgrade)
{
    flag = true;
    Upgrade_panel.SetActive(false);

    for (int i = 0; i < Inventory.transform.childCount; i++)
    {
        if (Inventory.transform.GetChild(i).GetComponent<Image>().enabled == false && flag)
        {
            Inventory.transform.GetChild(i).GetComponent<Image>().enabled = true;
                Inventory.transform.GetChild(i).GetComponent<Image>().sprite= sprite;
            flag = false;
        }
    }
    switch (upgradeChosen)
    {
        case "Multiple shot":
            Debug.Log("multiple shot");
            break;
        case "Attack Speed":
            Debug.Log("Attack speed");
            break;
        case "Armour":
            Debug.Log("Armour");
            break;
        case "Damage":
            Debug.Log("Damage");
            break;
        case "Piercing Shot":
            Debug.Log("Piercing Shot");
            break;
        case "Bouncing Shot":
            Debug.Log("Bouncing Shot");
            break;


    }

    Time.timeScale = 1;
    
   

}
private void ShuffleList(List<int> list)
{
    for (int i = 0; i < list.Count; i++)
    {
        int RandomIndex = Random.Range(i, list.Count);
        int temp = list[i];
        list[i] = list[RandomIndex];
        list[RandomIndex] = temp;
    }
}

}

You seem to be only adding listeners to the buttons, so yes these buttons will keep accruing listeners and multiple things will happen after the first time.

Seeing as you’re subscribing via a lambda function, you can’t easily unsubscribe your existing anonymous functions from them.

What I would suggest to do is write another component that you attached to your buttons. You can pass it an upgrade, and it has its own callback that returns said callback.

For example:

public class UpgradeButton : MonoBehaviour
{
    [SerializeField]
    private Text _upgradeNameText;
    
    [SerializeField]
    private Text _upgradeDescriptionText;
    
    [SerializeField]
    private Image _upgradeSprite;
    
    [SerializeField]
    private Button _button;
    
    private Upgrade _upgrade;
    
    public event System.Action<Upgrade> OnUpgradeSelected;
    
    private void Awake()
    {
        _button.AddListener(HandleUpgradeSelected);
    }
    
    public void SetUpgrade(Upgrade upgrade)
    {
        _upgradeNameText.text = upgrade.Name;
        _upgradeDescriptionText.text = upgrade.Description;
        _upgradeSprite.text = _upgrade.Sprite;
        _upgrade = upgrade;
    }
    
    private void HandleUpgradeSelected()
    {
        OnUpgradeSelected?.Invoke(_upgrade);
    }
}

Additionally in this case you can simply assign references to the UI components via the inspector. There’s no need for the fragile Transform.GetChild calls.

Then you only need to subscribe to each button’s OnUpgradeSelected callback once, and can simply pass it a different upgrade UpgradeButton.SetUpgrade each time before you open it.

I can also suggest that you make your Upgrade class serializable by decorating it with [System.Serializable], so you can edit it via the inspector rather than hard code everything.

1 Like

I didn’t know that about lambda. You gave me a light with that code, I did not use all of it, but gave me a idea.
I serialized the parameters(text, description, etc) as you did, and stored the name of the upgrade on a string variable.
Then i used a function to level up based on the upgrade name, and serialized the function on the button.
Is working pretty great, at least until now.

[SerializeField] public Text upgradeName;
[SerializeField] public Text upgradeDescription;
[SerializeField] public Sprite upgradeSprite;
[SerializeField] private Button _button;
[SerializeField] public GameObject upgrade_panel;

public string _upgradeName;


void Start()
{
    
}


void Update()
{
    _upgradeName = upgradeName.text;
}

public void UpgradeChoose()
{
    Time.timeScale = 1f;
    switch (_upgradeName)
    {
        case "Multiple shot":
            Debug.Log("multiple shot");

            break;
        case "Attack Speed":
            Debug.Log("Attack speed");

            break;
        case "Armour":
            Debug.Log("Armour");

            break;
        case "Damage":
            Debug.Log("Damage");

            break;
        case "Piercing Shot":
            Debug.Log("Piercing Shot");

            break;
        case "Bouncing Shot":
            Debug.Log("Bouncing Shot");

            break;
    }
    upgrade_panel.SetActive(false);
}

Thank you so much for the tip!