Menu
CoddyTech

Un singleton genérico

Parte de la sección Arquitectura de juegos en Unity del Journey de Unity de Coddy. Lección 43 de 64.

El singleton de la sección 3 son cinco líneas que repite cada administrador: un Instance estático, un Awake que lo establece y la comprobación de duplicados. Una clase base genérica las escribe una sola vez. Cada administrador deriva de ella y se nombra como el tipo:

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); }
}
// en cualquier script
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

Un miembro estático de una clase genérica pertenece a cada tipo creado a partir de ella: Singleton<ScoreManager> y Singleton<SoundManager> son dos tipos, cada uno con su propio Instance, así que los administradores nunca comparten uno. ScoreManager.Instance ya tiene el tipo ScoreManager, que es para lo que sirve T. Dentro de la clase base, this solo se conoce como un Singleton<T>, así que this as T lo entrega como el administrador que realmente es.

La clase base es abstracta: Unity coloca solo los administradores en los objetos. Un administrador que necesite su propio Awake no debe escribir un void Awake() simple. Eso oculta el método de la clase base (el compilador lo advierte), Unity llama solo al del administrador y Instance nunca se establece. Anúlalo y llama primero al de la clase base:

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

Desafío

Intermedio

Dales a ambos administradores una base que persista al cargar escenas.

  1. En PersistentSingleton, escribe protected virtual void Awake(): el primero se convierte en Instance y permanece (DontDestroyOnLoad); uno posterior se destruye a sí mismo.
  2. Wallet y MusicPlayer heredan de esta clase; elimina su Instance y el Awake de Wallet.
  3. MusicPlayer sobrescribe Awake: base.Awake() y, después, si es la Instance, registra Now playing: Adventure.

Pulsa «Jugar», visita el bosque y vuelve: 1 moneda. Después, pulsa «Comprobar».

Pruébalo tú mismo

using UnityEngine;

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

    // Awake: conserva el primero al cargar otras escenas y destruye cualquier instancia posterior
}

Todas las lecciones de Arquitectura de juegos en Unity

Practica por tu cuenta: Playground de Unity