Menu
CoddyTech

Um Singleton Genérico

Parte da seção Arquitetura de jogos no Unity do Journey de Unity da Coddy. Lição 34 de 50.

O singleton da seção 3 consiste em 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 as escreve 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, e é para isso que serve T. Dentro da classe base, this é conhecido apenas como um Singleton<T>, então this as T o entrega como o gerenciador que realmente é.

A classe base é abstrata: o Unity coloca apenas os gerenciadores nos objetos. Um gerenciador que precisa de seu próprio Awake 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 do gerenciador e Instance nunca é definido. Faça o override e chame primeiro a classe base:

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

Desafio

Médio

Uma cidade e uma floresta, conectadas por portais roxos. Cada cena tem seu próprio objeto Wallet e MusicPlayer, então cada um toca por conta própria, e as moedas chamam Wallet.Instance.Add(1). Hoje, cada gerenciador é um singleton da seção 3, então cada carregamento substitui a carteira por uma vazia. Dê aos dois gerenciadores uma classe base genérica que os mantenha durante o carregamento das cenas:

  • Em PersistentSingleton.cs, escreva public abstract class PersistentSingleton<T>, um MonoBehaviour para qualquer tipo T de MonoBehaviour, com um Instance estático público do tipo T que só a classe pode definir, e um protected virtual void Awake().
  • O Awake mantém a primeira instância do seu tipo: ela se torna Instance e sobrevive ao carregamento das cenas. Qualquer instância posterior destrói o próprio objeto e não altera nada.
  • Wallet e MusicPlayer derivam dessa classe, e os campos Instance próprios deles devem ser removidos.
  • MusicPlayer mantém seu próprio Awake, que registra Now playing: Adventure. Ele deve ser executado depois do Awake da classe base, e somente para a instância que permanece, para que a música comece uma vez por jogo.

As setas movem o Player. Pressione Jogar, pegue a moeda, vá até a floresta e volte, depois Verificar.

Experimente você mesmo

using UnityEngine;

// PersistentSingleton<T>: uma instância que persiste durante o carregamento de cenas
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

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