Обобщённый синглтон
Часть раздела Архитектура игр в 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;
}Задание
СреднеГород и лес, соединённые фиолетовыми порталами. В каждой сцене есть собственные объекты 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>: один экземпляр, который сохраняется при загрузке сцен
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Архитектура игр в Unity
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыСовместная работа компонентовПовторение: модульная турель4Сериализованные данные
Сериализуемые классыСписки в InspectorПеречисления в InspectorПовторение: таблица лута2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействия5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиВолны как данныеПовторение: база предметов8Переиспользуемый код
Методы расширенияОбобщённый синглтонОбобщённые вспомогательные методыПовторение: FindNearest