Создание игры Arkanoid на PyGame Zero за 30 минут

Создание игры Arkanoid на PyGame Zero

В данной статье мы расскажем, как при помощи Python и PyGame Zero можно быстро и легко написать клон игры Арканоид (Breakout).

Игра Arkanoid на Python

Содержание статьи

Что ж, приступим!

Весь код, который будет использоваться в данном руководстве, доступен по ссылке.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Паблик VK

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Изображения были взяты с Kenney. На данном сайте есть множество клевых бесплатных ассетов для создания игры. Обязательно зацените его!

Установка Pygame Zero

Первым делом нужно создать пустое окно:

Здесь мы импортируем Pygame Zero. Переменная TITLE — это заголовок отображаемого окна, а переменные WIDTH и HEIGHT определяют ширину и высоту данного окна соответственно.

Метод pgzrun.go() запускает программу.

Вы должны увидеть пустое окно:

Игра Арканоид на Python

Давайте попробуем отобразить несколько игровых блоков.

Добавим следующий код под высотой и шириной:

Actor отвечает за то, как Pygame Zero будет показывать картинки. Изображения всегда хранятся в папке images, которая находится в том же месте, где расположен скрипт игры.

Мы также указываем начальные позиции x и y для тех изображений, которые будут загружены:

Эти значения могут быть любыми, я получил вышеуказанные числа методом проб и ошибок.

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

По сути, у нашей игры будет 60 FPS.

Функции update() и draw() в Pgzero

Функции update и draw похожи — разница лишь в том, что update вызывается 60 раз в секунду, в то время как draw вызывается только в тех случаях, когда что-то нужно изменить, нарисовать…

Здесь нет никаких строгих правил, но я бы использовал функцию update для вещей, которые часто меняются. Например, для стенки с кирпичиками и мячика. В то время как для фоновых изображений можно использовать draw.

Создание двигающейся панели и мячика

Для начала воспользуемся функцией draw(), а update() пока оставим пустой:

Игра Арканоид на Python

Давайте сделаем еще кое-что. Вам не кажется, что сейчас фон очень скучный? Давайте сделаем его немного повеселее.

Обновим функцию draw():

В качестве фона мы используем файл background.png, который находится в папке images. Функция blit() рисует наше изображение на экране. Кортеж (0,0) является стартовой позицией, где x=0 и y=0. Систему координат в Pygame Zero мы обсудим немного позже.

Игра Арканоид на Python

Намного лучше!

Создание стенки с кирпичиками для игры Арканоид

Сейчас мы займемся созданием стенки с кирпичиками, которых будет сбивать мяч.

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

Игра Арканоид на Python

Очевидно, что строить стенку по одному кирпичику  за раз будет очень долго и муторно. Давайте используем цикл for, чтобы упростить задачу.

Здесь мы создаем начальные переменные для координат x и ybar_x инициализируется на 120, а bar_y — на 100.

Мы выполняем цикл 8 раз. Почему 8? Потому что именно столько кирпичиков мы можем удобно разместить на экране.

Для каждого цикла мы создаем объект Actor, инициализируем его координаты x и y и рисуем все на экране. Затем делаем следующее:

Таким образом, каждый следующий кирпичик будет передвигаться на 70 пикселей влево. Опять же, число 70 было выбрано методом проб и ошибок. Можете попробовать изменить это значение и посмотреть, если кирпичики будут «налезать» друг на друга или когда они будут слишком далеко друг от друга.

Запустим код:

Игра Арканоид на Python

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

Первым делом я хочу превратить приведенный выше код в удобную функцию:

Здесь я поместил использованный раннее код в функцию place_blue_bars().

Я мог бы создать больше функций наподобие create_red_bars() и так далее, но, думаю, будет лучше использовать более умный подход.

Итак, у нас будет общая функция place_bars():

В верхней части мы также создадим еще одну глобальную переменную bars_list = []. Мы будем использовать ее для проверки того, какие кирпичики отображать, а какие удалять после попадания в них мячика.

Передадим начальные координаты x и y первого кирпичика, а также изображение, которое мы будем использовать. Конечная функция выглядит следующим образом:

Единственное изменение заключается в том, что мы инициализируем x, y и изображение из полученных входных данных.

Мы вызовем эту функцию до запуска основного кода игры, то есть перед pgzero.run():

У нас есть список с 3 изображениями. Мы инициализируем значения x и y. Затем делаем цикл for по списку:

Нам нужно сделать y += 50 в каждом цикле, иначе кирпичики будут располагаться друг над другом.

Окончательный код:

Нам нужно сделать еще кое-что. Создадим кирпичики, но отображать их не будем. Обновим функцию draw():

Игра Арканоид на Python

Теперь у нас есть красивый макет. Наконец-то можно заняться логикой!

Добавление физики для двигающейся панели

Давайте начнем с добавления физики для нижней панели. Нам нужно, чтобы игрок мог двигать данный элемент туда-сюда, стараясь отбить мячик. В Pygame Zero это сделать очень просто — вы можете просто проверить события клавиатуры напрямую. Давайте обновим функцию update():

if keyboard.left проверяет, нажата ли левая стрелка на клавиатуре, и если да, то изменяет x-позицию двигающейся панели на -5 (то есть перемещает ее влево на 5 пикселей). И то же самое происходит для правой стрелки на клавиатуре.

Почему я выбрал 5 пикселей? Чтобы найти баланс между слишком быстрым/медленным перемещением. Попробуйте изменить значения на 1 и 10 и посмотрите, что получится.

Нажмите на левую и правую стрелки — теперь вы можете перемещать нижнюю панель.

Игра Арканоид на Python

Добавление физики для движения мячика в игре

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

Мы создадим новую функцию update_ball(), которую будем вызывать из функции update().

Таким образом мы меняем позиции x и y для мяча.

Система координат в Pygame Zero

Система координат в PyGame Zero

Координаты верхней левой части экрана — 0, 0 то есть x=0, y=0.

Если вы двигаетесь вправо, то значение x возрастает.

Если вниз, то возрастает значение y.

При движении влево, уменьшается x. При движении вправо, увеличивает x.

Для передвижения вниз, увеличиваем y. Вверх — уменьшаем y.

Учитывая все это:

ball.x -= 1 двигает мяч влево (так как -1 = влево, +1 = вправо)

ball.y -= 1 двигает мяч вверх (так как -1 = вверх, +1 = вниз)

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

Игра в Pygame Zero

Мяч за пределами экрана! Вот незадача!

Нам нужно добавить проверки, чтобы при ударе о стены мяч отскакивал назад. Это физическая часть.

Давайте добавим проверку.

Сначала добавим глобальную переменную для скорости x и y. Добавьте эти глобальные переменные в верхнюю часть файла:

Скорость равна 1 пикселю для движения влево/вправо и вверх/вниз. Вы можете попробовать увеличить это число, чтобы заставить мяч двигаться быстрее (и, следовательно, увеличить сложность игры), но мы будем придерживаться значения 1, так как это упрощает тестирование.

Давайте используем эту переменную в нашей функции:

Код такой же, как и раньше, просто мы заменяем ‘1’ на переменную. Теперь добавим проверки.

Если x превышает максимальное значение WIDTH, которое мы определили для игры (то есть мяч выходит за правую часть экрана), или меньше 0 (то есть выходит за левую часть экрана), тогда делаем следующее:

Что это значит? Помните, что при каждом обновлении мы двигались со скоростью мяча. Сначала движения были вверх и влево.

Здесь мы умножаем скорость на -1. Таким образом, если мяч двигался влево, он начнет двигаться вправо, и наоборот.

В результате, как только мяч достигнет границы, он изменит направление.

Мы можем сделать то же самое для оси y:

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

Протестируем код:

Игра на Pygame Zero

Клево, теперь мячик отскакивает от стенок. Однако он по-прежнему проходит сквозь стенку с кирпичиками. Давайте исправим это.

Коллизия (обнаружения столкновений) объектов в Pygame Zero

Для обнаружения столкновения добавим следующий код в функцию update():

Давайте разберем каждую строчку кода:

Используем цикл для кирпичиков:

И для каждого кирпича мы проверяем, столкнулся ли с ним мяч:

colliderect() — это встроенная функция, которая проверяет, столкнулись ли два объекта. В данном случае — мяч и кирпич.

Если они столкнулись, мы удаляем кирпич из списка:

Вы помните, что кирпичики создаются с помощью функции draw()?

Если мы удалим кирпич из списка, он больше не будет показываться и, следовательно, исчезнет с экрана.

Запустите код, чтобы проверить его работоспособность:

Игра Арканоид в Pygame Zero

Окей, уже лучше. Вот только наш мяч проходит сквозь кирпичи, будто нож сквозь масло. Нам такого не нужно. Игра Арканоид устроена таким образом, что мячик должен отскакивать от кирпичей, когда касается их.

К счастью, нашу проблему можно решить очень легко:

Последняя строка — это новый код. Мы изменяем направление мячика, так что если он поднимался вверх, то теперь начнет опускаться вниз.

Есть еще одна вещь, которую я бы хотел сделать. В оригинальной игре Арканоид, когда мячик ударялся о кирпичики или двигающуюся нижнюю панель, он мог лететь как влево, так и вправо, имитируя реальную «физику», похожую на физику пинбола. Да, это было не идеально, но это добавляло веселья в игру, так как вы не знали, куда полетит мяч.

Для этого я добавлю следующий код:

Когда мяч попадает в какой-то блок, мы случайным образом, примерно в 50% случаев, меняем направление. Так, если мяч двигался вправо, он может начать двигаться влево.

Осталось сделать последний штрих.

Отскакивание мяча от двигающей панели в игре на Pygame Zero

Я просто поделюсь с вами кодом — думаю, вы поймете все сами:

Мы снова проверяем, столкнулся ли мяч с нижней панелью, и если да, меняем направление по оси y. Мы также случайным образом меняем направление x.

Теперь в вашем распоряжении есть простенькая, но рабочая игрушка:

Игра Арканоид на Python

Следующие шаги

Помните, что вы всегда можете скачать код с сайта https://github.com/shantnu/arkanoid-clone.

Чтобы проверить, насколько вы поняли код, попробуйте внести следующие изменения:

  1. В коде есть серьезный баг — если мячик падает ниже нижней панели, игра продолжается. По сути, вы никогда не можете проиграть!Вам нужно изменить логику таким образом, чтобы если мячик опускался ниже нижней двигающей панели, игра заканчивалась. Мы рассмотрим, как создать экран завершения игры в следующем примере, а пока просто переключайте игрока на консоль.
  2. Попробуйте добавить очки — каждый раз, когда игрок попадает в кирпич, он получает 1 очко. Опять же, просто выводите счет на консоль.Чтобы получить бонусные очки, для разноцветных кирпичей можно использовать разные баллы. Подсказка: вам нужно будет хранить кирпичи в другом списке, чтобы вы могли проверять количество очков в зависимости от того, какой цвет вы сбили.