Часть №10: Система бонусов в игре «Breakout» на С++/OpenGL

  Дмитрий Бушуев  | 

  Обновл. 20 Дек 2020  | 

 2098

Наша разработка игры «Breakout» близится к завершению, но было бы здорово добавить еще хотя бы одну новую игровую механику, не так ли?

Бонусы в игре «Breakout»

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

Мы можем смоделировать бонусы при помощи класса GameObject с несколькими дополнительными свойствами. Именно поэтому мы определяем класс PowerUp, который наследует класс GameObject:

Как уже было сказано, класс PowerUp — это тот же класс GameObject, но с дополнительным свойством-состоянием, поэтому мы можем определить его просто в одном заголовочном файле — power_up.h:

Каждый бонус характеризуется своим типом в виде строковой переменной, продолжительностью действия, и переменной, которая сообщает — активен ли бонус в данный момент или нет. В рамках нашей игры мы собираемся реализовать в общей сложности 4 позитивных и 2 негативных бонуса:

   speed — увеличивает скорость мяча на 20%;

   sticky — когда мяч сталкивается с ракеткой, он прилипает к ней до того момента, пока игрок не нажмет пробел. Это позволяет игроку выбрать новую наиболее удачную позицию для мяча, прежде чем продолжить игру;

   pass-through — определение столкновений отключено для нетвердых кирпичей, что позволяет мячу проходить сквозь них;

   pad-size-increase — увеличивает ширину ракетки на 50 пикселей;

   confuse — активирует на короткий период времени эффект постобработки «Confuse», сбивая игрока с толку;

   chaos — активирует на короткий период времени эффект постобработки «Chaos», сильно дезориентируя игрока.

Вы можете найти используемые текстуры здесь:

   Speed

   Sticky

   Pass-Through

   Pad-Size-Increase

   Confuse

   Chaos

Подобно текстурам кирпичей уровней, цветовая гамма каждой из текстур бонусов представлена в виде оттенков серого. Благодаря этому, при умножении на цветовой вектор цвет бонусов остается сбалансированным.

Поскольку бонусы имеют состояние, длительность и определенные эффекты, связанные с типом бонуса, то мы хотели бы отслеживать все активные на данный момент бонусы; мы сохраним их в векторе:

Мы также определили две функции для управления бонусами:

   функция Game::SpawnPowerUps() — добавляет бонус в месте заданного блока;

   функция Game::UpdatePowerUps() — управляет всеми активными в текущий момент игры бонусами.

Добавление бонусов


При каждом разрушении кирпича существует небольшой шанс создания бонуса. Эту возможность можно реализовать при помощи функции Game::SpawnPowerUps():

Функция Game::SpawnPowerUps() с некоторой вероятностью (1 из 75 — для позитивных бонусов и 1 из 15 — для негативных бонусов) создает новый объект класса PowerUp и устанавливает ему соответствующие свойства. Каждому бонусу присваивается определенный цвет, чтобы сделать его более узнаваемым для пользователя, и продолжительность действия в секундах, зависящую от типа бонуса; длительность 0.0f означает, что его продолжительность является бесконечной. Кроме того, каждому бонусу присваивается местоположение разрушенного кирпича и одна из текстур, представленных в начале этого урока.

Активация бонусов

Теперь мы должны обновить функцию Game::DoCollisions(), чтобы не только проверить наличие столкновений между мячом и ракеткой, но и наличие столкновений между ракеткой и бонусом. Обратите внимание, что мы вызываем функцию SpawnPowerUps() непосредственно после уничтожения кирпича:

Для каждого бонуса, который еще не уничтожен, мы проверяем — достиг ли он нижнего края экрана или столкнулся ли с ракеткой игрока. В обоих случаях бонус уничтожается, но при столкновении с ракеткой он еще и активируется.

Активация бонуса осуществляется путем установки его свойства Activated в значение true и включения соответствующего эффекта с помощью функции ActivatePowerUp():

Название функции ActivatePowerUp() говорит само за себя: она активирует эффект от бонуса (напомню, список эффектов представлен в начале урока). Мы проверяем тип бонуса и соответствующим образом меняем состояние игры. Для эффектов «Sticky» и «Pass-Through» мы также меняем цвет ракетки и мяча, чтобы дать пользователю некоторую обратную связь относительно того, какой эффект в данный момент активен.

Поскольку два вышеупомянутых эффекта несколько изменяют логику игры, то мы сохраняем их эффекты в качестве свойств мяча; таким образом, мы можем изменить логику игры, в зависимости от того эффекта, который в данный момент применен к мячу. Единственное, что нужно изменить в заголовочном файле класса BallObject, — это добавить пару свойств.

#Класс BallObject

Заголовочный файл — ball_object.h:

Файл реализации — ball_object.cpp:

Затем мы можем реализовать эффект «Sticky», слегка обновив функцию DoCollisions() в коде определения столкновения между мячом и ракеткой:

Здесь мы устанавливаем свойство мяча Stuck равное свойству Sticky. Если активирован эффект «Sticky», то мяч будет прилипать к ракетке всякий раз, когда он с ней сталкивается; игрок должен снова нажать пробел, чтобы запустить мяч.

Аналогичное небольшое изменение в той же самой функции DoCollisions() сделано и для эффекта «Pass-Through». Когда свойство мяча PassThrough установлено в true, мы не выполняем никакой обработки столкновения на нетвердых кирпичах:

Другие эффекты (например, изменение скорости мяча, размера ракетки или применение эффектов «Chaos» и «Confuse») активируются простым изменением состояния игры.

Обновление бонусов


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

В рамках функции Game::UpdatePowerUps() мы перемещаем бонусы в зависимости от их скорости и уменьшаем продолжительность действия активных в текущий момент бонусов. Всякий раз, когда длительность бонуса становится равной 0.0f, его эффект деактивируется, и соответствующие переменные сбрасываются в исходное состояние:

Вы можете видеть, что для каждого эффекта реализовано его отключение, при этом соответствующие элементы сбрасываются в исходное состояние. Мы также устанавливаем свойство бонуса Activated в false. В конце функции Game::UpdatePowerUps() мы проходимся циклом по вектору класса PowerUps и стираем каждый бонус, если он уничтожен и деактивирован. Мы используем функцию remove_if() из заголовочного файла algorithm, чтобы стереть элементы вектора при помощи лямбда-выражений.

Примечание: Функция remove_if() перемещает все элементы, на которых лямбда-выражение имеет значение true, в конец объекта контейнера и возвращает итератор, указывающий на начало диапазона удаленных элементов. Затем контейнерная функция erase() использует этот итератор и итератор конца вектора для удаления всех элементов между данными итераторами.

Может случиться так, что в то время, когда активирован один из бонусных эффектов, другой бонус того же типа столкнется с ракеткой игрока. В таком случае у нас есть более 1 бонуса заданного типа, который в настоящее время активен в векторе класса PowerUps. При этом, всякий раз, когда один из этих бонусов отключается, мы не хотим отключать его эффект, так как другой бонус того же типа все еще может быть активен. По этой причине мы используем функцию IsOtherPowerUpActive(), чтобы проверить — есть ли еще один активный бонус того же типа. Только если эта функция возвращает false, мы отключаем бонус. Таким образом, продолжительность бонусов данного типа увеличивается до продолжительности последнего активированного бонуса данного типа:

Функция проверяет наличие всех активированных бонусов и возвращает true, если находит активные бонусы одинакового типа.

Последнее, что осталось сделать — это визуализировать бонусы:

Совместив все эти функции, мы получим работающую систему бонусов и усилений, которая не только делает игру более увлекательной, но и намного более сложной:

  GitHub / Часть №10: Система бонусов и усилений в игре «Breakout» на С++/OpenGL — Исходный код

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (4 оценок, среднее: 5,00 из 5)
Загрузка...

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *