Résolution des problèmes de référence des boutons Unity causés par les types références

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.

Étiquettes: Unity C# delegates reference types button events

Publié le 29 août à 10h33