Menu
CoddyTech

Um Singleton Genérico

Parte da seção Arquitetura de Jogos no Unity do Journey de Unity da Coddy. Lição 43 de 64.

O singleton da Seção 3 tem cinco linhas que cada gerenciador repete: um Instance estático, um Awake que o define e a verificação de duplicatas. Uma classe-base genérica escreve essas linhas uma vez. Cada gerenciador deriva dela, nomeando a si mesmo como o 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); }
}
// em qualquer script
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

Um membro estático de uma classe genérica pertence a cada tipo criado a partir dela: Singleton<ScoreManager> e Singleton<SoundManager> são dois tipos, cada um com seu próprio Instance, então os gerenciadores nunca compartilham um. ScoreManager.Instance já tem o tipo ScoreManager, que é para isso que serve T. Dentro da classe-base, this só é conhecido como um Singleton<T>, então this as T o entrega como o gerenciador que ele realmente é.

A classe-base é abstrata: o Unity coloca apenas os gerenciadores nos objetos. Um gerenciador que precisa de um Awake próprio não deve escrever um void Awake() simples. Isso oculta o método da classe-base (o compilador avisa sobre isso), o Unity chama apenas o método do gerenciador, e Instance nunca é definido. Sobrescreva-o e chame primeiro o método da classe-base:

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

Desafio

Médio

Dê aos dois gerenciadores uma base que persista ao carregar cenas.

  1. Em PersistentSingleton, escreva protected virtual void Awake(): o primeiro se torna Instance e permanece (DontDestroyOnLoad); um posterior se destrói.
  2. Wallet e MusicPlayer derivam dele; exclua o Instance deles e o Awake de Wallet.
  3. MusicPlayer sobrescreve Awake: base.Awake() e, em seguida, se for o Instance, registra Now playing: Adventure.

Pressione “Jogar”, visite a floresta e volte: 1 moeda. Depois, pressione “Verificar”.

Experimente você mesmo

using UnityEngine;

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

    // Awake: mantenha o primeiro entre os carregamentos de cena e destrua qualquer outro que surgir depois
}

Todas as lições de Arquitetura de Jogos no Unity

Pratique por conta própria: Playground de Unity