Урок №15. Создаём игру «Арканоид» в Qt5/С++

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

  Обновл. 31 Янв 2020  | 

 886

 ǀ   4 

Разработка компьютерной игры — это отличный способ узнать больше о Qt5. Поэтому сегодня мы попробуем создать свою версию известной аркадной игры под названием «Арканоид» (англ. «Breakout»), разработанной в 1976 году не менее известной компанией Atari Inc. В этой игре игрок перемещает подвижную ракетку и пытается отбросить летящий к нему мяч в верхнюю часть экрана, где расположена группа кирпичей/блоков. Цель состоит в том, чтобы, отбивая мяч, уничтожить все блоки.

Разработка игры «Арканоид»

Для начала давайте перечислим основные моменты игры. У нас есть одна подвижная Ракетка, один Мяч и тридцать Кирпичей/Блоков. Ещё нам потребуется таймер, который будет использоваться для создания игрового цикла. Чтобы сделать проект как можно проще и понятнее, мы не будем рассматривать вопросы реализации начисления игровых очков, бонусов и пр.

Итак, начнём с заголовочного файла для объекта paddle (подвижная Ракетка). INITIAL_X и INITIAL_Y — это константы, которые представляют начальные координаты объекта paddle.

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

Как уже было упомянуто, Ракетка может перемещаться только вправо или влево.

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

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

Метод move() перемещает прямоугольник с изображением Ракетки. Направление движения задаётся переменной dx:

Функция resetState() устанавливает Ракетку в исходное положение:

Теперь рассмотрим заголовочный файл для объекта brick (Кирпич/Блок). Если Кирпич разрушен, то значением переменной destroyed становится true.

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

Класс Brick представляет объект brick.

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

Конструктор класса Brick загружает изображение Кирпича, инициирует переменную-флаг destroyed и устанавливает изображение в исходную позицию:

У объектов класса Brick есть переменная-флаг destroyed. Если её значение установлено как true, то Кирпич считается разрушенным, и не отображается в окне:

Переходим к заголовочному файлу объекта ball (Мяч). В переменных xdir и ydir хранится направление движения Мяча.

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

В начале игры Мяч движется в направлении вправо-вверх:

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

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

Ракетка управляется с помощью кнопок-стрелок на клавиатуре. В игре мы следим за событиями нажатия кнопок клавиатуры с помощью методов ниже:

В переменной x хранится текущее положение Ракетки по оси X. Переменная timerId используется для идентификации объекта timer. Это необходимо в тех моментах, когда мы приостанавливаем игру:

Константа N_OF_BRICKS задаёт количество Кирпичей в игре:

Константа DELAY управляет скоростью игры:

Когда Мяч пересекает нижний край окна, то игра заканчивается:

Переменные-указатели на объекты Мяча, Ракетки и массива Кирпичей:

Следующие четыре переменные отвечают за различные состояния игры:

В файле breakout.cpp находится логика нашей игры.

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

В конструкторе класса Breakout мы создаём экземпляр тридцати Кирпичей:

В зависимости от переменных gameOver и gameWon мы либо заканчиваем игру, выдавая соответствующие сообщения, либо продолжаем отрисовывать в окне игровые объекты:

Метод finishGame() отображает завершающее сообщение в центре окна. Это либо "Game lost" ("Игра проиграна"), либо "Victory" ("Победа"). Метод QFontMetrics::width() используется для вычисления ширины строки соответствующего сообщения:

Метод drawObjects() отрисовывает в окне все объекты игры: Мяч, Ракетку и Кирпичи. А так как данные объекты представлены изображениями, то при помощи метода drawImage() мы отображаем и их изображения:

В теле метода timerEvent() мы сначала перемещаем объекты, затем проверяем, не столкнулся ли Мяч с Ракеткой или Кирпичом, и в конце генерируем событие отрисовки:

Метод moveObjects() отвечает за перемещения объектов Мяч и Ракетка. В нём вызываются их собственные методы перемещения:

Когда игрок отпускает кнопку или , то мы устанавливаем переменную dx Ракетки в ноль. В результате Ракетка перестаёт двигаться:

В методе keyPressEvent() мы отслеживаем события нажатия клавиш, относящиеся к нашей игре. Кнопки и перемещают объект Ракетки. Они влияют на значение переменной dx, которое затем будет добавлено к координате х самой Ракетки. Кнопка P ставит игру на паузу, кнопка "Пробел" запускает игру, а кнопка Esc завершает работу приложения:

Метод startGame() сбрасывает состояния объектов ball и paddle; они перемещаются в исходное положение. В цикле for мы устанавливаем флаг destroyed на false для каждого Кирпича, таким образом, отображая их в окне. Переменные gameOver, gameWon и gameStarted получают свои начальные логические значения. Наконец, с помощью метода startTimer(), мы запускаем таймер:

Функция pauseGame() используется для приостановки и запуска уже остановленной игры. Данное состояние игры управляется с помощью переменной paused. Также мы храним и идентификатор таймера. Чтобы приостановить игру, мы «убиваем» таймер с помощью метода killTimer(). Чтобы перезапустить его, мы вызываем метод startTimer():

В методе stopGame() мы «убиваем» таймер и устанавливаем соответствующие флаги:

В методе checkCollision() мы делаем проверку столкновения Мяча с игровыми объектами. Игра заканчивается, если Мяч попадает в нижний край окна:

Проверяем количество разрушенных Кирпичей. Если все Кирпичи уничтожены, то мы выиграли:

Если Мяч попадает в верхнюю часть Ракетки, то меняем направление Мяча на влево-вверх:

Если Мяч ударяется о нижнюю часть Кирпича, то мы меняем направление y Мяча — он идёт вниз.

Главный файл программы — main.cpp:

Результатом является игра «Арканоид»:


Заключение


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

До скорой встречи в следующий уроках 😉

Исходный код игры «Арканоид» в Qt5/C++.

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

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

Комментариев: 4

  1. Аватар Мгер:

    И еще почему код не в гит-репозитории? можно было бы изменять, дополнять и тд)

    1. Дмитрий Бушуев Дмитрий Бушуев:

      Тогда и статью нужно будет переместить в гит-репозиторий 🙂

  2. Аватар Мгер:

    Спасибо автору, очень интересно читать.
    Хочется продолжения: модель-представлений, таблиц, QML, работы с потоками, может мобильная разработка?

    1. Дмитрий Бушуев Дмитрий Бушуев:

      Насчет продолжения — продолжение будет.
      P.S.: Всегда пожалуйста. 🙂

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

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