Тетрис на C++: Соблюдение правил

Тетрис на C++ Соблюдение правил

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

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

Правила тетриса довольно просты:

  1. Игра начинается с появления случайного падающего элемента в верхней центральной части игрового поля. Этот элемент может контролировать игрок;
  2. Когда активный элемент касается своей нижней частью дна игрового поля или занятого блока, то через несколько игровых шагов он фиксируется и его позиция больше не подвластна игроку;
  3. Если в игровом поле образуются полностью заполненные ряды из непустых блоков, то они исчезают, а верхние ряды сдвигаются вниз;
  4. После каждого фиксирования игрового элемента появляется новый, как в начале игры;
  5. Если вновь появившийся элемент сразу имеет столкновения, то есть находится в некорректном состоянии, то игра окончена;
  6. Выиграть в тетрис, к сожалению, нельзя.

А теперь посмотрим, как все эти проверки можно записать с помощью кода C++.

Появление нового элемента

При инициализации модели активный игровой элемент находится в нейтральном состоянии с пустой внутренней матрицей (см. Паттерн Null Object). Такая проверка выполняется с помощью функции-члена isNull():

Формирование нового элемента выполняем в начале функции-члена doStep(), которая вызывается для каждого игрового шага (по таймеру из Контроллера):

Формирование случайного элемента осуществляется в функции TetrisItem::generateRandom():

Проверка Game Over

В функцию doStep() удобно добавить и проверку на окончание игры:

Спросить у модели о том, не окончена ли игра, можно с помощью isGameOver():

Активный элемент на дне

Когда нижняя часть активного элемента во что-то упирается, то мы позволяем ему немного «поползать»:

Для поддержки функции перемещения по дну нам пришлось добавить счетчик касаний. Каждый раз когда элемент касается непустого блока своей нижней частью, значение счетчика увеличивается на единицу. Как только значение счетчика достигает определенной границы, элемент фиксируется (точнее, пока что просто исчезает). Если же игрок перемещает элемент (влево или вправо) таким образом, что он вновь начинает падать, то значение счетчика сбрасывается.

Фиксирование активного элемента

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

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

Очистка заполненных рядов и повышение сложности

Модель практически закончена. Остается добавить всего несколько заключительных штрихов. Вот алгоритм очистки игрового поля от заполненных рядов:

Вызывать clean() нужно сразу же после кода фиксирования элемента:

За каждый убранный ряд количество набранных очков увеличивается на единицу. За каждые набранные 10 очков сложность игры возрастает (путем наращивания скорости на единицу):

На самом деле, функция incScore() получилась не самой удачной (см. Принцип единой ответственности): она имеет побочный эффект (увеличивает скорость). Однако для такой простой игры это оказалось не критично. Возможно, что в более сложном проекте пришлось бы ее переработать.

Последняя мелочь

Скорость падения элемента может меняется не только в результате вызова incSpeed(), но и по воле игрока. Для этого усовершенствуем нашу модель следующим образом:

Заключение

На этом мы завершаем знакомство с Моделью игры тетрис. А в следующий раз переходим к краткому обзору реализаций Представления и Контроллера

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