Menu
CoddyTech

Un singleton générique

Fait partie de la section Architecture de jeu dans Unity du Journey Unity de Coddy. Leçon 34 sur 50.

Le singleton de la section 3 consiste en cinq lignes que chaque gestionnaire répète : un Instance statique, un Awake qui le définit, et la vérification des doublons. Une classe de base générique les écrit une seule fois. Chaque gestionnaire en dérive en se désignant comme type :

public abstract class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this as T;
    }
}

public class ScoreManager : Singleton<ScoreManager>
{
    public int score;
    public void Add(int points) { score += points; }
}

public class SoundManager : Singleton<SoundManager>
{
    public void Play(string clip) { Debug.Log("Playing " + clip); }
}
// dans n’importe quel script
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

Un membre statique d'une classe générique appartient à chaque type créé à partir de celle-ci : Singleton<ScoreManager> et Singleton<SoundManager> sont deux types qui ont chacun leur Instance, donc les gestionnaires ne partagent jamais la même. ScoreManager.Instance est déjà typé ScoreManager, et c'est à cela que sert T. Dans la classe de base, this n'est connu que comme un Singleton<T>, donc this as T le transmet sous la forme du gestionnaire qu'il est réellement.

La classe de base est abstraite : Unity ne place que les gestionnaires sur les objets. Un gestionnaire qui a besoin de son propre Awake ne doit pas écrire un simple void Awake(). Cela masque la méthode de la classe de base (le compilateur émet un avertissement), Unity n'appelle que celle du gestionnaire, et Instance n'est jamais défini. Remplacez la méthode et appelez d'abord celle de la classe de base :

protected override void Awake()
{
    base.Awake();
    score = 0;
}
challenge icon

Défi

Moyen

Une ville et une forêt, reliées par des portails violets. Chaque scène possède son propre objet Wallet et MusicPlayer, si bien que l’un ou l’autre joue de son côté, et les pièces appellent Wallet.Instance.Add(1). Aujourd’hui, chaque gestionnaire est le singleton de la section 3 : à chaque chargement, le portefeuille est remplacé par un portefeuille vide. Donne aux deux gestionnaires une classe de base générique qui les conserve lors des chargements de scène :

  • Dans PersistentSingleton.cs, écris public abstract class PersistentSingleton<T>, un MonoBehaviour pour n’importe quel type MonoBehaviour T, avec une propriété statique publique Instance de type T que seule la classe peut définir, et une méthode protected virtual void Awake().
  • Son Awake conserve la première instance de son type : celle-ci devient Instance et survit aux chargements de scène. Toute instance ultérieure détruit son propre objet et ne change rien d’autre.
  • Wallet et MusicPlayer en héritent, et leurs propres champs Instance disparaissent.
  • MusicPlayer conserve son propre Awake, qui affiche Now playing: Adventure dans les journaux. Il doit s’exécuter après l’Awake de la classe de base, et uniquement pour l’instance qui reste, afin que la musique démarre une seule fois par partie.

Les flèches déplacent le Player. Appuie sur Play, ramasse la pièce, va dans la forêt puis reviens, et enfin appuie sur Check.

Essayez vous-même

using UnityEngine;

// PersistentSingleton<T> : une instance qui persiste lors des chargements de scènes
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Architecture de jeu dans Unity