Menu
CoddyTech

Обобщённый синглтон

Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 43 из 64.

Синглтон из раздела 3 — это пять строк, которые повторяет каждый менеджер: статический Instance, Awake, который задаёт его, и проверка на дубликат. Обобщённый базовый класс записывает их один раз. Каждый менеджер наследуется от него, указывая своё имя в качестве типа:

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); }
}
// в любом скрипте
ScoreManager.Instance.Add(10);
SoundManager.Instance.Play("coin");

Статический член обобщённого класса принадлежит каждому созданному на его основе типу: Singleton<ScoreManager> и Singleton<SoundManager> — это два типа, у каждого из которых есть собственный Instance, поэтому менеджеры никогда не используют один общий. ScoreManager.Instance уже имеет тип ScoreManager — именно для этого нужен T. Внутри базового класса this известен только как Singleton<T>, поэтому this as T передаёт его как тот тип менеджера, которым он на самом деле является.

Базовый класс абстрактный: Unity размещает на объектах только менеджеры. Если менеджеру нужен собственный Awake, нельзя объявлять обычный void Awake(). Этот метод скроет метод базового класса (компилятор предупредит об этом), Unity вызовет только метод менеджера, и Instance так и не будет задан. Переопределите метод и сначала вызовите базовый:

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

Задание

Средне

Создай для обоих менеджеров базовый класс, который сохраняется при загрузке сцен.

  1. В PersistentSingleton напиши protected virtual void Awake(): первый экземпляр становится Instance и сохраняется (DontDestroyOnLoad); следующий уничтожает сам себя.
  2. Wallet и MusicPlayer наследуются от него; удали их Instance и Awake из Wallet.
  3. В MusicPlayer переопредели Awake: base.Awake(), затем, если он является Instance, выведи в журнал Now playing: Adventure.

Нажми «Играть», сходи в лес и вернись: 1 монета. Затем нажми «Проверить».

Попробуйте сами

using UnityEngine;

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

    // Awake: сохранять первый экземпляр при загрузке сцен, а все последующие уничтожать
}

Все уроки раздела Архитектура игр в Unity

Потренируйтесь самостоятельно: Песочница Unity