Архитектурные шаблоны Umbra

Материал из Клуб любителей рогаликов
Перейти к: навигация, поиск

Архитектурные шаблоны. Как ваш код сделать управляемым

Цель этой статьи — познакомить вас с архитектурными шаблонами, которые используются в Umbra, но также применимые в любых других приложениях.

Движок модулей или Кукловод

Основной шаблон проектирования, использующийся в Umbra, я назвал (за неимением более подходящего имени) — Кукловод. Суть его работы в разделении игрового кода на одну или несколько частей, называемых UmbraModule, которые запускаются и управляются главным одиночным объектом — UmbraEngine.

Общую идею можно описать так: движок, действующий как кукловод, дёргает за ниточки свои куклы — модули. Движок берёт на себя заботу об основном цикле игры, где он не выполняет никаких игровых задач, но только управляет модулями.

На каждой итерации движок должен выполнить следующие задачи:

  1. Деактивировать модули, находящиеся в списке toDeactivate
  2. Активировать модули, находящиеся в списке toActivate
  3. Очистить экран (если нужно)
  4. Принять события мыши и клавиатуры
  5. Пройти все активные модули и в каждом из них:
    • Обработать ввод с клавиатуры из принятых событий
    • Обработать ввод с мыши из принятых событий
    • Обновить их внутреннюю логику
    • Вывести на экран всё то, что должно выводится в этом модуле
  6. Обновить содержимое экрана (показать всё, что активные модули нарисовали)

Модули, или куклы, это части. То есть, они реализуют объединённый интерфейс, управляемый движком. Каждый из них реализует как минимум четыре метода, вызывающиеся при каждой итерации игрового цикла. Эти методы отвечают за:

  1. Обработку ввода с клавиатуры в данном модуле
  2. Обработку ввода с мыши в данном модуле
  3. Обновление внутренней логики модуля
  4. Вывод модуля на экран

Реализация каждого из этих четырёх методов лежит на разработчике, поэтому они могут использовать любые ресурсы и любые методы, доступные в этом модуле.

Примерный набор модулей может быть таким:

  • Персонаж игрока. Обрабатывает управление персонажем, обновляет его место на карте, отправляет запросы взаимодействия с другими объектами карты и/или персонажами.
  • Персонажи. Содержит список всех персонажей и обновляет действия их ИИ, в зависимости от принятых решений.
  • Карта. Выводит на экран игровую карту, вместе с персонажем игрока и всеми персонажами в области видимости.
  • Окно сообщений. Хранит буфер сообщений, выводит на экран последние из них.
  • Экран характеристик. Отображает все данные, связанные с персонажем игрока (его характеристики, здоровье и т.п.)

Архитектура модуля "Ввод-Обработка-Вывод"

Главная идея здесь в чётком разделении на фазы действий. Работа модуля делится на три полунезависимых фазы:

  1. Обработка ввода и немедленное реагирование (например, получили нажатие клавиши и тут же выполнили соответствующее этой клавише действие)
  2. Обновление внутренней логики
  3. Вывод данных после их обработки и возможных изменений

Фаза обработки ввода вполне очевидна, поэтому я пропущу её описание.

Фаза обновления логики работает с любыми нужными и доступными данными. Они должны храниться либо внутри модуля, который и будет сам себя обновлять, либо (если нужно) во внешних объектах, не являющихся модулями. Получение данных из других модулей в этой фазе довольно опасно, потому что эти данные могут измениться до наступления фазы вывода.

Фаза вывода не обрабатывает никаких данных, а только использует их для создания содержимого экрана. На этом этапе получение данных из других модулей наиболее безопасно, поскольку фазы обработки всех модулей уже закончились. С другой стороны, изменение данных технически возможно, но далеко небезопасно — опять же, потому что они могут понадобиться другим модулям.

Преимущества

Преимущества этих шаблонов довольно очевидны, но я всё же их перечислю:

  • Повторное использование кода. Так как движок абсолютно независим от его модулей, то его можно написать только раз. И позже использовать во множестве проектов. К модулям это тоже относится. Например, модуль персонажа игрока можно использовать в других играх сразу же, либо с небольшими изменениями (адаптациями к требованиям конкретной игры).
  • Заменяемость кода. В большинстве случаев, неисправный модуль можно безопасно убрать и заменить, не нарушая работы всей игры. Например, модуль отображения характеристик может быть отключен, либо полностью убран из игры, при этом на работу игры это не повлияет — просто полоски с данными не будут выводиться на экран. Модуль можно заменить на что-нибудь ещё. Скажем, если у нас имеется два или больше варианта модуля вывода характеристик, из которых игрок может выбрать подходящий для себя.
  • Независимость кода. Так как модули достаточно независимы друг от друга, то работая над одним модулем можно не опасаться, что поломается что-то ещё.
  • Удобочитаемость кода. Благодаря объединённой архитектуре, код становится более лёгким для чтения. А значит, им проще управлять, даже когда новый разработчик вступает в команду или берёт проект на себя.

Недостатки

Как защитник описанных шаблонов, я не вижу очевидных недостатков. Хотя, полагаю, может быть некоторое падение производительности, особенно когда есть множество небольших, но специализированных модулей. Например, модуль управления ИИ не обрабатывает ввод и не выводит ничего на экран. Однако, его фазы отрисовки и обработки вывода, всё же будут вызываться — на это будет тратиться некоторое количество работы процессора. Но это только гипотетически, потому что никаких тестов производительности в поддержку или в опровержение этого не проводилось.

Другая возможная проблема состоит в увеличении используемой памяти. Для настольных компьютеров это увеличение может и не будет заметным. Но для портативных устройств, где ввиду небольшого количества памяти за ней строгий присмотр, придётся подумать, чтобы успешно реализовать этот шаблон.



Автор: mingos
Источник: Architectural patterns. How to make your code manageable
Перевод: Sanja, 29.09.2011