Тетрис на C++: Представление и Контроллер

Тетрис на C++ Представление и Контроллер

В прошлый раз мы закончили с Моделью тетриса. Однако чтобы получить игру, одной логики мало. Требуется взаимодействие с пользователем и координация действий приложения. Первую задачу решает Представление, а вторую — Контроллер. Ими мы и займемся.

Предыдущие части:

  1. Тетрис на C++: Введение
  2. Тетрис на C++: Статическая модель
  3. Тетрис на C++: Обнаружение столкновений
  4. Тетрис на C++: Динамическая модель
  5. Тетрис на C++: Соблюдение правил

Представление для тетриса

Представление находится на передовой приложения. Именно с ним взаимодействует пользователь. Представление обеспечивает:

  1. Визуализацию данных Модели;
  2. Перенаправление действий пользователя Контроллеру.

Чтобы соблюдать чистоту паттерна MVC, придерживайтесь простого правила: Представление не меняет состояние Модели. Поэтому если заметили, что Представление использует не- const функцию-член Модели, то что-то не так.

Визуализация данных Модели

В качестве базового класса Представления используем обычный QWidget. Не самое удачное решение для игрового приложения, но вполне уместное для демонстрации принципов. Неплохим выбором на этот случай является QGLWidget. Возможно, в будущем мы до него еще дойдем.

Отрисовку Модели выполняем с помощью QPainter в обработчике paintEvent():

Реализация Представления, отвечающая за визуализацию, решает 3 подзадачи:

  1. Рисует сетку игрового поля, полезную в режиме отладки;
  2. Рисует каждый блок игрового поля;
  3. Рисует каждый блок активного элемента.

Для рисования блоков используется отдельная функция drawBlock(), которая принимает координаты середины блока в точках, тип блока и указатель на QPainter. Каждому типу блока ставится в соответствие некоторый цвет. Проще всего для этого использовать таблицу цветов, представленную вектором. В этом случае для типа блока type цвет выбирается следующим образом: COLOR_TABLE[ type - 1 ].

Одним из способов вызова paintEvent() является слот repaint(). Однако не будем привязываться к нему жестко и создадим свою обертку:

В нашем случае такое решение является избыточным, но для мест стыка между компонентами MVC это необходимо. Ведь на то он и MVC, что интерфейс Представления должен быть независимым (конечно, для этого нужно ввести базовый абстрактный класс, что мы не делали для краткости). Кроме того, требования постоянно меняются и реализация функции refresh() в какой-то момент может усложниться даже при наличии единственного класса Представления.

Вызов refresh() в нужные моменты обеспечит Контроллер (альтернатива — паттерн Наблюдатель в рамках Модели). При этом мы не мелочимся, а перерисовываем все поле каждый раз.

Перенаправление действий пользователя Контроллеру

Тетрис — не самая сложная игра в плане управления. Свяжем события нажатий нужных клавиш с соответствующими обработчиками Контроллера:

Все изменения в Модели должны происходить только через Контроллер.

Контроллер для тетриса

Контроллер в рамках MVC представляет собой прослойку между Представлением и Моделью, когда речь заходит о действиях пользователя. При правильном проектировании реализация функций Контроллера представляется тривиальной задачей:

Здесь следует обратить внимание на следующие моменты:

  • Вызов onStep() происходит по событию таймера QTimer. В onStep() выполняется обновление состояния модели с помощью doStep(). Таким образом, в Модели отсутствует понятие паузы. Пауза достигается путем остановки таймера. С одной стороны, это не очень хорошо, поскольку Контроллеру не желательно иметь собственного состояния. С другой стороны, паттерны проектирования не являются жесткими решениями, поэтому всегда в первую очередь руководствуйтесь здравым смыслом;
  • Также в onStep() проверяется не окончена ли игра. Если игра окончена, то на консоль выводится счет, и игра перезапускается;
  • Обработка игровых действий выполняется с помощью закрытой функции onAction(), которая принимает указатель на функцию-член Модели. Это удобно, поскольку в обработке действий много общего. Например, если игра приостановлена, то действие выполнять не требуется. К тому же, после выполнения действия не помешает обновить Представление, чтобы оно отражало актуальное состояние Модели.

Заключение

На этом мы завершаем цикл статей, посвященных разработке тетриса на C++. Нельзя сказать, что мы реализовали законченную игру. Ей не хватает еще многих элементов: таблицы лучших результатов, графических и звуковых эффектов, и т.д. Однако то, что у нас получилось, является хорошей основой. А принятые проектные решения обеспечивают относительно простое расширение функционала.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий