Menu
CoddyTech

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

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

Синглтон из раздела 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

Задание

Средне

Город и лес, соединённые фиолетовыми порталами. В каждой сцене есть собственные объекты Wallet и MusicPlayer, поэтому каждый из них воспроизводит музыку самостоятельно, а монеты вызывают Wallet.Instance.Add(1). Сейчас каждый менеджер — это одиночка из раздела 3, поэтому при каждой загрузке кошелёк заменяется пустым. Создай для обоих менеджеров обобщённый базовый класс, который сохранит их при загрузке сцен:

  • В PersistentSingleton.cs напиши public abstract class PersistentSingleton<T> — MonoBehaviour для любого типа T, производного от MonoBehaviour, с общедоступным статическим Instance типа T, который может задавать только сам класс, и методом protected virtual void Awake().
  • Его Awake сохраняет первый объект своего типа: он становится Instance и переживает загрузку сцен. Каждый последующий объект уничтожает сам себя и ничего не меняет.
  • Wallet и MusicPlayer наследуются от него, а их собственные поля Instance удаляются.
  • У MusicPlayer остаётся собственный Awake, который выводит в журнал Now playing: Adventure. Он должен выполняться после Awake базового класса и только для того объекта, который остаётся, чтобы музыка запускалась один раз за игру.

Стрелки перемещают Player. Нажми Play, подбери монету, отправляйся в лес и обратно, затем нажми Check.

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

using UnityEngine;

// PersistentSingleton<T>: один экземпляр, который сохраняется при загрузке сцен
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

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