Problème identifié
Lors du développement d'un projet Unity, un comportement inattendu a été observé avec les boutons : lorsque plusieurs boutons écoutent une fonction utilisant une variable de type string, tous les boutons finissent par référencer la dernière valeur assignée à cette variable.
Reproduction du scénario problématique
Créons un script nommé GestionnaireBoutons pour illustrer cette situation :
using UnityEngine;
using UnityEngine.UI;
public class GestionnaireBoutons : MonoBehaviour
{
private string message;
[SerializeField] private Button premierBouton;
[SerializeField] private Button secondBouton;
[SerializeField] private Button troisiemeBouton;
void Start()
{
message = "premier message";
premierBouton.onClick.AddListener(() => AfficherMessage(message));
message = "second message";
secondBouton.onClick.AddListener(() => AfficherMessage(message));
message = "troisième message";
troisiemeBouton.onClick.AddListener(() => AfficherMessage(message));
}
private void AfficherMessage(string texte)
{
Debug.Log(texte);
}
}
Après avoir créé trois boutons dans l'interface Canvas et assigné les références dans l'inspecteur, le comportement attendu serait :
- Bouton 1 : "premier message"
- Bouton 2 : "second message"
- Bouton 3 : "troisième message"
Cependant, le résultat réel montre que tous les boutons affichent "troisième message".
Solution implémentée
La solution consiste à utiliser des variables distinctes pour chaque bouton :
using UnityEngine;
using UnityEngine.UI;
public class GestionnaireBoutons : MonoBehaviour
{
[SerializeField] private Button premierBouton;
[SerializeField] private Button secondBouton;
[SerializeField] private Button troisiemeBouton;
void Start()
{
string msg1 = "premier message";
premierBouton.onClick.AddListener(() => AfficherMessage(msg1));
string msg2 = "second message";
secondBouton.onClick.AddListener(() => AfficherMessage(msg2));
string msg3 = "troisième message";
troisiemeBouton.onClick.AddListener(() => AfficherMessage(msg3));
}
private void AfficherMessage(string texte)
{
Debug.Log(texte);
}
}
Explication technique
Ce comportement est dû à la nature des types références en C#. Les délégués capturent la référence à la variable plutôt que sa valeur au moment de la création. Lorsque la variable est modifiée, toutes les références pointent vers la valeur la plus récente.
Les types références concernés incluent :
- string (chaînes de caractères)
- tableaux ([])
- classes personnalisées
- List<T> (collections génériques)
- dynamic (type dynamique)
- interfaces, événements et délégués
Il est essentiel de comprendre ce mécanisme pour éviter des références involontaires dans les systèmes d'événements Unity.