Senior Unity Developer Всем привет! Меня зовут Игорь Гулькин, и я Unity разработчик. За свои 5 лет накопилось много опыта, поэтому в этой статье хотел бы поделиться принципами и подходами, с помощью которых можно реализовать архитектуру игры просто и гибко без фреймворка. Цель доклада, дать не просто готовое решение, а показать ход мыслей, как она выстраивается.  Ну что ж поехали :)   Давайте предположим, что мы делаем игру, где управляем кубиком с помощью клавиатуры. На сцене есть GameObject’ы:  Player — кубик, которым игрок управляет. KeyboardInput — пользовательский ввод с клавиатуры. MoveController — соединяет пользовательский ввод и вызывает у кубика Player.Move(). Вот начальные скрипты этих классов: Скрипт кубика: Скрипт пользовательского ввода: Скрипт контроллера перемещения: Игра работает, но есть несколько архитектурных недостатков: Нет точки входа в игру и соответственно завершения. Все зависимости на классы проставляются вручную через инспектор. Вся игровая логика привязана к монобехам (MonoBehaviour). Нет порядка инициализации игры. Давайте улучшать нашу архитектуру по порядку. Все мы знаем, что игра — это процесс у которого есть состояния. Есть состояние загрузки игры, старта, паузы и завершения. Практически во всех играх необходимо сделать так, чтобы этим состоянием можно было управлять. Поэтому будет здорово, если KeyboardInput и MoveController будут включаться по событию старта игры, а не при запуске PlayMode в Unity. Тогда дизайн класса KeyboardInput будет выглядит так: А класс MoveController будет выглядеть так: Здесь мы видим, что KeyboardInput и MoveController реализуют интерфейсы IStartGameListener и IFinishGameListener. Через эти интерфейсы, компоненты системы получают сигналы об изменении состояния игры. Сюда сразу же можно прикрутить еще два интерфейса: IPauseGameListener, IResumeGameListener. Они указывают, когда игра переходит в состояние паузы и наоборот. Ниже приложил код всех 4-х интерфейсов: Таким образом, используя принцип Interface Segregation компоненты будут обрабатывать только те состояния игры, которые они реализуют Теперь кто-то должен сообщать интерфейсам об изменении состояния игры. Тут мы можем обратиться к паттерну Наблюдатель и реализовать класс-приемник, который будет получать сигналы об изменении фазы игры. Структура приемника будет следующей: Отлично! Давайте теперь добавим скрипт GameObservable на сцену: Если нажать на “три точки” рядом с этим скриптом, то можем увидеть, что у этого скрипта можно вызывать методы запуска, паузы и завершения игры. Нажав на волшебную кнопочку “Play” в Unity, мы видим, что перемещение кубика по нажатию клавиатуры не работает. Почему, спросите вы? Да потому что компоненты KeyboardInput и MoveController не подключены к монобеху GameObservable в качестве наблюдателей. Поэтому нам нужен класс, который зарегистрирует KeyboardInput и MoveController в приемник GameObservable. Назовем этот класс GameObservableInstaller. Тут все очень просто: инсталлер содержит в себе ссылку на приемник и массив с другими монобехами, которые реализуют интерфейсы состояний игры. В методе Awake() регистрируем все лисенеры в приемник. Затем добавляю скрипт GameObservableInstaller на сцену и подключаю ему лисенеры:  Теперь нужно проверить, что все работает! Запускаю PlayMode в Unity. Вызываю в контекстном меню приемника GameObservable метод StartGame. Нажимаю на клавиатуру и вижу, что “кубик поехал”. Вуаля, все работает! Дополнительным бонусом, можем проверить, что если вызвать метод GameObservable.FinishGame(), то KeyboardInput и MoveController перестанут работать. Все хорошо, но есть пара нюансов: Нет возможности узнать текущее состояние игры. Можно вызывать события игры в любом порядке (типа “пауза” после “окончания” и т.д.). Давайте доработаем наш приемник: Первым делом, думаю, вы заметили, что добавился enum GameState в котором, указано перечисление возможных состояний игры. Во-вторых, наш замечательный скрипт GameObservable переименовался в GameMachine. Это связано с тем, что наш текущий класс занимается уже не рассылкой событий, а переключает состояние игры в целом. Таким образом, у нас получился механизм, с помощью которого мы можем управлять состоянием игры и оповещать об этом компоненты системы. На этом первая часть статьи закончилась. Читать вторую часть. В завершение приглашаю вас на бесплатный урок, где изучим паттерн Model-View-Adapter в упрощенном варианте без пользовательского ввода на примере виджета монет игрока. Зарегистрироваться на бесплатный урок  Редактор