Arcade: Учебное пособие по игровому фреймворку Python

Оглавление

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

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

Библиотека arcade - это современный фреймворк на Python для создания игр с привлекательной графикой и звуком. Объектно-ориентированный и созданный для Python версии 3.6 и выше, arcade предоставляет программисту современный набор инструментов для создания великолепных игр на Python.

В этом руководстве вы узнаете, как:

  • Установите библиотеку arcade
  • Нарисуйте элементов на экране
  • Работайте с arcade игровым циклом Python
  • Управление графическими элементами на экране
  • Обрабатывать пользовательский ввод
  • Воспроизведение звуковых эффектов и музыки
  • Опишите, чем программирование игр на Python с помощью arcade отличается от pygame

В этом руководстве предполагается, что вы разбираетесь в написании программ на Python. Поскольку arcade является объектно-ориентированной библиотекой, вы также должны быть знакомы с объектно-ориентированным программированием. Весь код, изображения и звуки для этого руководства доступны для скачивания по ссылке ниже:

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

Предыстория и настройка

Библиотека arcade была написана Полом Винсентом Крейвеном, профессором компьютерных наук в Симпсон-колледже в Айове, США. Поскольку он построен поверх библиотеки окон и мультимедиа pyglet, arcade содержит различные улучшения, модернизацию и усовершенствования по сравнению с pygame:

  • Обладает современной графикой OpenGL
  • Поддерживает Python 3 подсказки типа
  • Улучшена поддержка анимированных спрайтов
  • Содержит согласованные названия команд, функций и параметров
  • Способствует отделению игровой логики от кода отображения
  • Требуется меньше шаблонного кода
  • Содержит больше документации, включая полные примеры игр на Python .
  • Имеет встроенный физический движок для платформенных игр

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

$ python -m pip install arcade

На компьютере Mac вам также необходимо установить PyObjC:

$ python -m pip install PyObjC arcade

Полные инструкции по установке, основанные на вашей платформе, доступны для Windows, Mac, Linux и даже Raspberry Pi. Вы даже можете установить arcade непосредственно из исходного кода, если хотите.

Примечание: В более поздних версиях arcade используются классы данных, которые включены в Python только для версии 3.7 и более поздних версий.

Однако в PyPI для Python 3.6 доступен резервный порт, который вы можете установить с помощью pip:

$ python -m pip install dataclasses

Смотрите Полное руководство по классам данных в Python 3.7 для получения дополнительной информации.

В этом руководстве предполагается, что вы используете arcade версии 2.1 и Python 3.7.

Основные arcade Программа

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

 1# Basic arcade program
 2# Displays a white window with a blue circle in the middle
 3
 4# Imports
 5import arcade
 6
 7# Constants
 8SCREEN_WIDTH = 600
 9SCREEN_HEIGHT = 800
10SCREEN_TITLE = "Welcome to Arcade"
11RADIUS = 150
12
13# Open the window
14arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
15
16# Set the background color
17arcade.set_background_color(arcade.color.WHITE)
18
19# Clear the screen and start drawing
20arcade.start_render()
21
22# Draw a blue circle
23arcade.draw_circle_filled(
24    SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RADIUS, arcade.color.BLUE
25)
26
27# Finish drawing
28arcade.finish_render()
29
30# Display everything
31arcade.run()

Когда вы запустите эту программу, вы увидите окно, которое выглядит следующим образом:

A basic program using the arcade library.

Давайте разберем это построчно:

  • Строка 5 импортирует библиотеку arcade. Без этого больше ничего не работает.
  • Строки с 8 по 11 определяют некоторые константы, которые вы будете использовать чуть позже, для наглядности.
  • Строка 14 открывает главное окно. Вы задаете ширину, высоту и текст строки заголовка, а arcade делает все остальное.
  • Строка 17 устанавливает цвет фона, используя константу из пакета arcade.color. Вы также можете указать цвет RGB, используя список или кортеж .
  • Строка 20 переводит arcade в режим рисования. Все, что вы нарисуете после этой строки, будет отображено на экране.
  • Строки с 23 по 25 нарисуйте окружность, указав координаты центра по X и Y, радиус и цвет, который нужно использовать.
  • Строка 28 завершает режим рисования.
  • Строка 31 отображает ваше окно, которое вы можете видеть.

Если вы знакомы с pygame, то вы заметите некоторые отличия:

  • Нет pygame.init(). Вся инициализация выполняется при запуске import arcade.
  • Явно определенного цикла отображения нет. Это обрабатывается в arcade.run().
  • Здесь также нет цикла обработки событий. Опять же, arcade.run() обрабатывает события и обеспечивает некоторые действия по умолчанию, такие как возможность закрыть окно.
  • Вы можете использовать заранее определенные цвета для рисования, а не определять их все самостоятельно.
  • Вы должны начать и закончить рисование в аркаде, используя start_render() и finish_render().

Давайте внимательно рассмотрим фундаментальные arcade концепции, лежащие в основе этой программы.

arcade Концепции

Например pygame, arcade код выполняется практически на каждой платформе, поддерживающей Python. Для этого требуется arcade иметь дело с абстракциями для различных аппаратных различий на этих платформах. Понимание этих концепций и абстракций поможет вам проектировать и разрабатывать свои собственные игры, а понимание того, чем arcade отличается от pygame, поможет вам адаптироваться к их уникальной точке зрения.

Инициализация

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

  • Убедитесь, что вы работаете на Python версии 3.6 или выше.
  • Импортируйте библиотеку pyglet_ffmeg2 для обработки звука, если она доступна.
  • Импортируйте библиотеку pyglet для работы с окнами и мультимедиа.
  • Установите констант для цветов и сопоставлений клавиш.
  • Импортируйте оставшуюся arcade библиотеку.

Сравните это с pygame, который требует отдельного шага инициализации для каждого модуля.

Окна и координаты

Все в arcade происходит в окне, которое вы создаете с помощью open_window(). В настоящее время arcade поддерживает только одно окно отображения. Вы можете изменить размер окна при его открытии.

arcade использует ту же декартову систему координат, которую вы, возможно, изучали на уроках алгебры. Окно находится в квадранте I, а исходная точка (0, 0) расположена в левом нижнем углу экрана. Координата x увеличивается при движении вправо, а координата y увеличивается при движении вверх:

The layout of an arcade window.

Важно отметить, что такое поведение отличается от pygame и многих других игровых фреймворков на Python. Возможно, вам потребуется некоторое время, чтобы привыкнуть к этой разнице.

Рисунок

В готовом виде arcade содержит функции для рисования различных геометрических фигур, в том числе:

  • Дуги
  • Окружности
  • Эллипсы
  • Линии
  • Параболы
  • Точки
  • Многоугольники
  • Прямоугольники
  • Треугольники

Все функции рисования начинаются с draw_ и соответствуют определенной схеме именования и параметров. Существуют различные функции для рисования заполненных и очерченных фигур:

Some sample shapes drawn with arcade

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

  • draw_rectangle() ожидает значения координат x и y центра прямоугольника, ширины и высоты.
  • draw_lrtb_rectangle() ожидаются левая и правая координаты x, за которыми следуют верхняя и нижняя координаты y.
  • draw_xywh_rectangle() используются координаты x и y нижнего левого угла, за которыми следуют ширина и высота.

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

Объектно-ориентированный дизайн

По своей сути, arcade представляет собой объектно-ориентированную библиотеку. Подобно pygame, вы можете написать arcade код процедурно, как вы делали в примере выше. Однако реальная мощь arcade проявляется при создании полностью объектно-ориентированных программ.

Когда вы вызвали arcade.open_window() в приведенном выше примере, код создает объект arcade.Window за кулисами для управления этим окном. Позже вы создадите свой собственный класс на основе arcade.Window, чтобы написать полноценную игру на Python.

Сначала взгляните на исходный пример кода, в котором теперь используются объектно-ориентированные концепции, чтобы подчеркнуть основные различия:

# Basic arcade program using objects
# Displays a white window with a blue circle in the middle

# Imports
import arcade

# Constants
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 800
SCREEN_TITLE = "Welcome to Arcade"
RADIUS = 150

# Classes
class Welcome(arcade.Window):
    """Main welcome window
    """
    def __init__(self):
        """Initialize the window
        """

        # Call the parent class constructor
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)

        # Set the background window
        arcade.set_background_color(arcade.color.WHITE)

    def on_draw(self):
        """Called whenever you need to draw your window
        """

        # Clear the screen and start drawing
        arcade.start_render()

        # Draw a blue circle
        arcade.draw_circle_filled(
            SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RADIUS, arcade.color.BLUE
        )

# Main code entry point
if __name__ == "__main__":
    app = Welcome()
    arcade.run()

Давайте рассмотрим этот код построчно:

  • Строки с 1 по 11 такие же, как в предыдущем примере процедуры.

  • Строка 15 - это то место, где начинаются различия. Вы определяете класс с именем Welcome на основе родительского класса arcade.Window. Это позволяет при необходимости переопределять методы родительского класса.

  • Строки с 18 по 26 определяют метод .__init__(). После вызова родительского метода .__init__() с помощью super() для настройки окна необходимо задать цвет его фона, как вы делали ранее.

  • Строки с 28 по 38 определяют .on_draw(). Это один из нескольких Window методов, которые вы можете переопределить, чтобы настроить поведение вашей arcade программы. Этот метод вызывается каждый раз, когда arcade требуется отрисовать окно. Он начинается с вызова arcade.start_render(), за которым следует весь ваш код рисования. Однако вам не нужно вызывать arcade.finish_render(), так как arcade вызовет это неявно, когда .on_draw() завершится.

  • Строки с 41 по 43 являются основной точкой входа в ваш код. После того, как вы впервые создадите новый объект Welcome с именем app, вы вызываете arcade.run() для отображения окна.

Этот объектно-ориентированный пример является ключом к получению максимальной отдачи от arcade. Одна вещь, на которую вы, возможно, обратили внимание, - это описание .on_draw(). arcade будет вызывать эту функцию каждый раз, когда она захочет нарисовать что-либо в окне. Итак, как arcade узнает, когда что-либо рисовать? Давайте взглянем на последствия этого.

Игровой цикл

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

  1. Программа определяет, окончена ли игра. Если это так, то цикл завершается.
  2. Вводимые пользователем данные обрабатываются.
  3. Состояния игровых объектов обновляются в зависимости от таких факторов, как вводимые пользователем данные или время.
  4. В игре отображаются визуальные эффекты и воспроизводятся звуковые эффекты, основанные на новом состоянии.

В pygame вы должны явно настроить этот цикл и управлять им. В arcade для вас предусмотрен игровой цикл Python, инкапсулированный в вызов arcade.run().

Во время встроенного игрового цикла arcade вызывает набор Window методов для реализации всех перечисленных выше функций. Все названия этих методов начинаются с on_ и могут рассматриваться как обработчики задач или событий. Когда игровому циклу arcade необходимо обновить состояние всех игровых объектов Python, он вызывает .on_update(). Когда ему нужно проверить движение мыши, он вызывает .on_mouse_motion().

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

  • Ввод с клавиатуры: .on_key_press(), .on_key_release()
  • Ввод с помощью мыши: .on_mouse_press(), .on_mouse_release(), .on_mouse_motion()
  • Обновление игрового объекта: .on_update()
  • Рисование: .on_draw()

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

Основы геймдизайна на Python

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

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

Когда мой бывший коллега описывал проекты по разработке программного обеспечения, он как-то сказал: “Вы не знаете, что вы делаете, пока не узнаете, чего вы не делаете”. Имея это в виду, вот вот некоторые вещи, которые вы не будете рассматривать в этом руководстве:

  • Нет нескольких жизней
  • Нет подсчета очков
  • Нет возможности атаковать игрока
  • Нет повышения уровня
  • Нет персонажей-“боссов”

Вы можете попробовать свои силы в добавлении этих и других функций в свою собственную программу.

Импорт и константы

Как и в случае с любой программой arcade, вы начнете с импорта библиотеки:

# Basic arcade shooter

# Imports
import arcade
import random

# Constants
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Arcade Space Shooter"
SCALING = 2.0

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

Класс окна

Чтобы в полной мере воспользоваться игровым циклом arcade Python и обработчиками событий, создайте новый класс на основе arcade.Window:

35class SpaceShooter(arcade.Window):
36    """Space Shooter side scroller game
37    Player starts on the left, enemies appear on the right
38    Player can move anywhere, but not off screen
39    Enemies fly to the left at variable speed
40    Collisions end the game
41    """
42
43    def __init__(self, width, height, title):
44        """Initialize the game
45        """
46        super().__init__(width, height, title)
47
48        # Set up the empty sprite lists
49        self.enemies_list = arcade.SpriteList()
50        self.clouds_list = arcade.SpriteList()
51        self.all_sprites = arcade.SpriteList()

Ваш новый класс запускается точно так же, как в приведенном выше объектно-ориентированном примере. В строке 43 вы определяете свой конструктор, который принимает ширину, высоту и заголовок игрового окна и использует super() для передачи их родительскому классу. Затем вы инициализируете несколько пустых списков спрайтов в строках с 49 по 51. В следующем разделе вы узнаете больше о спрайтах и списках спрайтов.

Спрайты и списки спрайтов

В вашем игровом проекте на Python требуется один игрок, который начинает игру слева и может свободно перемещаться по окну. Также требуются враги (другими словами, более одного), которые случайным образом появляются справа и перемещаются влево. Хотя вы могли бы использовать команды draw_, чтобы нарисовать игрока и каждого врага, быстро стало бы трудно следить за тем, чтобы все было правильно.

Вместо этого в большинстве современных игр используются спрайты для отображения объектов на экране. По сути, спрайт - это двумерное изображение игрового объекта определенного размера, которое рисуется в определенном месте на экране. В arcade спрайты - это объекты класса arcade.Sprite, и вы будете использовать их как для изображения вашего игрока, так и врагов. Вы даже добавите несколько облаков, чтобы сделать фон более интересным.

Управлять всеми этими спрайтами может быть непросто. Вы создадите спрайт для одиночной игры, но также будете создавать многочисленных врагов и облачных спрайтов. Отслеживать их все - это задача для списка спрайтов. Если вы понимаете, как работают списки Python, то у вас есть инструменты для использования списков спрайтов arcade. Списки спрайтов делают больше чем просто держаться за все спрайты. Они обеспечивают три важных вида поведения:

  1. Вы можете обновить все спрайты в списке одним вызовом SpriteList.update().
  2. Вы можете нарисовать все спрайты в списке одним вызовом SpriteList.draw().
  3. Вы можете проверить, не сталкивался ли отдельный спрайт с каким-либо другим спрайтом в списке.

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

  1. Вы используете .enemies_list для обновления позиций противника и проверки на наличие столкновений.
  2. Вы используете .clouds_list для обновления местоположения облаков.
  3. Наконец, вы используете .all_sprites, чтобы нарисовать все.

Теперь список полезен ровно настолько, насколько полезны содержащиеся в нем данные. Вот как вы заполняете списки спрайтов:

53def setup(self):
54    """Get the game ready to play
55    """
56
57    # Set the background color
58    arcade.set_background_color(arcade.color.SKY_BLUE)
59
60    # Set up the player
61    self.player = arcade.Sprite("images/jet.png", SCALING)
62    self.player.center_y = self.height / 2
63    self.player.left = 10
64    self.all_sprites.append(self.player)

Вы определяете .setup(), чтобы инициализировать игру до известной начальной точки. Хотя вы могли бы сделать это в .__init__(), полезно использовать отдельный метод .setup().

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

После того, как вы зададите цвет фона в строке 58, вы определите спрайт игрока:

  • Строка 61 создает новый объект arcade.Sprite, указывая изображение для отображения и коэффициент масштабирования. Рекомендуется организовать ваши изображения в одну подпапку, особенно в крупных проектах.

  • Строка 62 устанавливает положение спрайта по оси y равным половине высоты окна.

  • Строка 63 устанавливает положение x для спрайта, располагая левый край на расстоянии нескольких пикселей от левого края окна.

  • Строка 64, наконец, использует .append() чтобы добавить спрайт в список .all_sprites вы будете использовать его для рисования.

В строках 62 и 63 показаны два разных способа позиционирования вашего спрайта. Давайте подробнее рассмотрим все доступные варианты позиционирования спрайта.

Расположение спрайтов

Все спрайты в arcade имеют определенный размер и положение в окне:

  • Размер , указанный в параметрах Sprite.width и Sprite.height, определяется графическим изображением, используемым при создании спрайта.
  • Позиция изначально задана так, чтобы центр спрайта, указанный в Sprite.center_x и Sprite.center_y, был равен (0,0) в окне.

Как только координаты .center_x и .center_y известны, arcade может использовать размер для вычисления Sprite.left, Sprite.right, Sprite.top, а также Sprite.bottom ребер.

Это также работает в обратном порядке. Например, если вы установите для Sprite.left заданное значение, то для arcade также будут пересчитаны остальные атрибуты позиции. Вы можете использовать любой из них, чтобы найти спрайт или переместить его в окне. Это чрезвычайно полезная и мощная характеристика arcade спрайтов. Если вы будете использовать их, то для вашей игры на Python потребуется меньше кода, чем pygame:

Arcade tutorial game with just a player

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

Функции планирования

arcade.schedule() предназначен именно для этой цели. Он принимает два аргумента:

  1. Имя функции для вызова
  2. Интервал времени ожидания между каждым вызовом, в секундах

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

66# Spawn a new enemy every 0.25 seconds
67arcade.schedule(self.add_enemy, 0.25)
68
69# Spawn a new cloud every second
70arcade.schedule(self.add_cloud, 1.0)

Теперь все, что вам нужно сделать, это определить self.add_enemy() и self.add_cloud().

Добавление врагов

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

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

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

 93def add_enemy(self, delta_time: float):
 94    """Adds a new enemy to the screen
 95
 96    Arguments:
 97        delta_time {float} -- How much time has passed since the last call
 98    """
 99
100    # First, create the new enemy sprite
101    enemy = arcade.Sprite("images/missile.png", SCALING)
102
103    # Set its position to a random height and off screen right
104    enemy.left = random.randint(self.width, self.width + 80)
105    enemy.top = random.randint(10, self.height - 10)

.add_enemy() принимает единственный параметр delta_time, который показывает, сколько времени прошло с момента последнего вызова. Это требуется в arcade.schedule(), и хотя вы не будете использовать это здесь, это может быть полезно для приложений, требующих расширенного выбора времени.

Как и в случае со спрайтом игрока, вы сначала создаете новый arcade.Sprite с изображением и коэффициентом масштабирования. Вы устанавливаете позицию, используя .left и .top, в произвольное положение где-то за пределами экрана справа:

Several enemies appearing on the screen

Это позволяет противнику плавно перемещаться по экрану, а не просто появляться на нем. Итак, как заставить его двигаться?

Движущиеся спрайты

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

Атрибут Sprite.velocity представляет собой кортеж, состоящий из изменений в позициях x и y. Вы также можете напрямую обращаться к Sprite.change_x и Sprite.change_y. Как упоминалось выше, каждый раз, когда спрайт обновляется, его .position изменяется на основе .velocity. Все, что вам нужно сделать в .add_enemy(), это установить скорость:

107# Set its speed to a random speed heading left
108enemy.velocity = (random.randint(-20, -5), 0)
109
110# Add it to the enemies list
111self.enemies_list.append(enemy)
112self.all_sprites.append(enemy)

После того, как вы установите произвольную скорость перемещения влево по строке 108, вы добавите нового врага в соответствующие списки. Когда вы позже позвоните sprite.update(), arcade, я разберусь с остальным:

Enemies flying by in the tutorial game

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

Удаление спрайтов

Поскольку ваши враги всегда перемещаются влево, их x-позиции всегда становятся меньше, а y-позиции всегда остаются неизменными. Таким образом, вы можете определить, что враг находится за пределами экрана, когда enemy.right меньше нуля, что соответствует левому краю окна. Как только вы определите, что враг находится за пределами экрана, вы вызываете enemy.remove_from_sprite_lists(), чтобы удалить его из всех списков, к которым он принадлежит, и удалить этот объект из памяти:

if enemy.right < 0:
    enemy.remove_from_sprite_lists()

Но когда вы выполняете эту проверку? Обычно это происходит сразу после перемещения спрайта. Однако помните, что было сказано ранее о .all_enemies списке спрайтов:

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

Это означает, что в SpaceShooter.on_update() вы вызовете enemies_list.update() для автоматической обработки перемещения противника, что, по сути, приводит к следующему:

for enemy in enemies_list:
    enemy.update()

Было бы неплохо, если бы вы могли добавить проверку за кадром непосредственно к вызову enemy.update(), и вы можете это сделать! Помните, что arcade - это объектно-ориентированная библиотека. Это означает, что вы можете создавать свои собственные классы на основе arcade классов и переопределять методы, которые вы хотите изменить. В этом случае вы создаете новый класс на основе arcade.Sprite и переопределяете только .update():

17class FlyingSprite(arcade.Sprite):
18    """Base class for all flying sprites
19    Flying sprites include enemies and clouds
20    """
21
22    def update(self):
23        """Update the position of the sprite
24        When it moves off screen to the left, remove it
25        """
26
27        # Move the sprite
28        super().update()
29
30        # Remove if off the screen
31        if self.right < 0:
32            self.remove_from_sprite_lists()

Вы определяете FlyingSprite как все, что будет летать в вашей игре, например, враги и облака. Затем вы переопределяете .update(), сначала вызывая super().update() для правильной обработки движения. Затем вы выполняете проверку за кадром.

Поскольку у вас новый класс, вам также нужно будет внести небольшое изменение в .add_enemy():

def add_enemy(self, delta_time: float):
    """Adds a new enemy to the screen

    Arguments:
        delta_time {float} -- How much time as passed since the last call
    """

    # First, create the new enemy sprite
    enemy = FlyingSprite("images/missile.png", SCALING)

Вместо того, чтобы создавать новый Sprite, вы создаете новый FlyingSprite, чтобы воспользоваться преимуществами нового .update().

Добавление облаков

Чтобы сделать вашу игру на Python визуально более привлекательной, вы можете добавить облака на небо. Облака летают по небу, как и ваши враги, поэтому вы можете создавать и перемещать их аналогичным образом.

.add_cloud() выполняется по той же схеме, что и .add_enemy(), хотя случайная скорость медленнее:

def add_cloud(self, delta_time: float):
    """Adds a new cloud to the screen

    Arguments:
        delta_time {float} -- How much time has passed since the last call
    """

    # First, create the new cloud sprite
    cloud = FlyingSprite("images/cloud.png", SCALING)

    # Set its position to a random height and off screen right
    cloud.left = random.randint(self.width, self.width + 80)
    cloud.top = random.randint(10, self.height - 10)

    # Set its speed to a random speed heading left
    cloud.velocity = (random.randint(-5, -2), 0)

    # Add it to the enemies list
    self.clouds_list.append(cloud)
    self.all_sprites.append(cloud)

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

Теперь ваша игра на Python выглядит немного более завершенной:

Clouds flying on the game window

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

Ввод с клавиатуры

Класс arcade.Window имеет две функции для обработки ввода с клавиатуры. Ваша игра на Python будет вызывать .on_key_press() всякий раз, когда нажимается клавиша, и .on_key_release() всякий раз, когда клавиша отпускается. Обе функции принимают два целочисленных параметра:

  1. symbol представляет собой фактическую клавишу, которая была нажата или отпущена.
  2. modifiers указывает, какие модификаторы были отключены. К ним относятся клавиши Shift, Ctrl и Alt.

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

  1. Четыре клавиши со стрелками для Вверх, Вниз, Слева и Справа
  2. Ключи I, J, K и L, который отображает значения Вверх, влево, вниз и вправо
  3. Для управления левой рукой используются клавиши W, A, S и D, которые также отображаются на "Вверх", "Влево", "Вниз" и "Вправо"

В этой игре вы будете использовать стрелки и I/J/K/L. Всякий раз, когда пользователь нажимает клавишу перемещения, спрайт игрока перемещается в указанном направлении. Когда пользователь отпускает клавишу перемещения, спрайт перестает двигаться в этом направлении. Вы также указываете способ выхода из игры, используя Q, и способ приостановки игры, используя P. Для этого вам необходимо реагировать на нажатия и отпускания клавиш:

  • При нажатии клавиши вызывается .on_key_press(). В этом методе вы проверяете, какая клавиша была нажата:
    • Если это Q, то вы просто выходите из игры.
    • Если это P, то вы устанавливаете флажок, указывающий на то, что игра приостановлена.
    • Если это клавиша перемещения, то вы устанавливаете значение игрока .change_x или .change_y соответственно.
    • Если это любой другой ключ, то вы игнорируете его.
  • Когда клавиша будет отпущена, вызовите .on_key_release(). И снова проверьте, какая клавиша была отпущена:
    • Если это клавиша перемещения, то вы устанавливаете значение игрока .change_x или .change_y равным 0 соответственно.
    • Если это любой другой ключ, то вы игнорируете его.

Вот как выглядит код:

134def on_key_press(self, symbol, modifiers):
135    """Handle user keyboard input
136    Q: Quit the game
137    P: Pause/Unpause the game
138    I/J/K/L: Move Up, Left, Down, Right
139    Arrows: Move Up, Left, Down, Right
140
141    Arguments:
142        symbol {int} -- Which key was pressed
143        modifiers {int} -- Which modifiers were pressed
144    """
145    if symbol == arcade.key.Q:
146        # Quit immediately
147        arcade.close_window()
148
149    if symbol == arcade.key.P:
150        self.paused = not self.paused
151
152    if symbol == arcade.key.I or symbol == arcade.key.UP:
153        self.player.change_y = 5
154
155    if symbol == arcade.key.K or symbol == arcade.key.DOWN:
156        self.player.change_y = -5
157
158    if symbol == arcade.key.J or symbol == arcade.key.LEFT:
159        self.player.change_x = -5
160
161    if symbol == arcade.key.L or symbol == arcade.key.RIGHT:
162        self.player.change_x = 5
163
164def on_key_release(self, symbol: int, modifiers: int):
165    """Undo movement vectors when movement keys are released
166
167    Arguments:
168        symbol {int} -- Which key was pressed
169        modifiers {int} -- Which modifiers were pressed
170    """
171    if (
172        symbol == arcade.key.I
173        or symbol == arcade.key.K
174        or symbol == arcade.key.UP
175        or symbol == arcade.key.DOWN
176    ):
177        self.player.change_y = 0
178
179    if (
180        symbol == arcade.key.J
181        or symbol == arcade.key.L
182        or symbol == arcade.key.LEFT
183        or symbol == arcade.key.RIGHT
184    ):
185        self.player.change_x = 0

В .on_key_release() вы проверяете только те клавиши, которые влияют на перемещение спрайта игрока. Нет необходимости проверять, были ли нажаты клавиши паузы или завершения.

Теперь вы можете перемещаться по экрану и немедленно выходить из игры:

Moving the player around the screen

Возможно, вам интересно, как работает функция паузы. Чтобы увидеть это в действии, вам сначала нужно научиться обновлять все ваши игровые объекты на Python.

Обновление игровых объектов

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

Поскольку arcade управляет игровым циклом Python, он также определяет, когда требуются обновления, вызывая .on_update(). Вы можете переопределить этот метод, чтобы обеспечить правильное поведение для вашей игры, включая движение в игре и другое поведение. Для этой игры вам нужно сделать несколько вещей, чтобы все правильно обновить:

  1. Вы проверяете, приостановлена ли игра. Если да, то вы можете просто выйти, чтобы больше никаких обновлений не происходило.
  2. Вы обновляете все свои спрайты, чтобы заставить их двигаться.
  3. Вы проверяете, не переместился ли спрайт игрока за пределы экрана. Если это так, то просто верните его обратно на экран.

На этом пока все. Вот как выглядит этот код:

189def on_update(self, delta_time: float):
190    """Update the positions and statuses of all game objects
191    If paused, do nothing
192
193    Arguments:
194        delta_time {float} -- Time since the last update
195    """
196
197    # If paused, don't update anything
198    if self.paused:
199        return
200
201    # Update everything
202    self.all_sprites.update()
203
204    # Keep the player on screen
205    if self.player.top > self.height:
206        self.player.top = self.height
207    if self.player.right > self.width:
208        self.player.right = self.width
209    if self.player.bottom < 0:
210        self.player.bottom = 0
211    if self.player.left < 0:
212        self.player.left = 0

В строке 198 вы проверяете, приостановлена ли игра, и просто возвращаете ее, если да. При этом весь оставшийся код пропускается, поэтому перемещения не будет. Все перемещения спрайтов обрабатываются строкой 202. Эта единственная строка работает по трем причинам:

  1. Каждый спрайт является членом списка self.all_sprites.
  2. Вызов для self.all_sprites.update() приводит к вызову .update() для каждого спрайта в списке.
  3. Каждый спрайт в списке имеет .velocity (состоящий из атрибутов .change_x и .change_y) и будет обрабатывать свои собственные движение, когда вызывается его .update().

Наконец, вы проверяете, находится ли спрайт игрока за пределами экрана в строках с 205 по 212, сравнивая края спрайтов с краями окна. Например, в строках 205 и 206, если self.player.top находится за пределами верхней части экрана, вы возвращаете self.player.top в верхнюю часть экрана. Теперь, когда все обновлено, вы можете рисовать все.

Рисунок на окне

Поскольку обновления игровых объектов происходят в .on_update(), представляется целесообразным, чтобы отрисовка игровых объектов выполнялась методом, называемым .on_draw(). Поскольку вы организовали все в списки спрайтов, ваш код для этого метода очень короткий:

231def on_draw(self):
232    """Draw all game objects
233    """
234    arcade.start_render()
235    self.all_sprites.draw()

Все отрисовки начинаются с вызова arcade.start_render() в строке 234. Как и при обновлении, вы можете отрисовать все свои спрайты сразу, просто вызвав self.all_sprites.draw() в строке 235. Теперь вам осталось поработать над последней частью вашей игры на Python, и это самая последняя часть первоначального проекта:

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

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

Обнаружение столкновений

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

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

Однако вам не обязательно писать свой собственный код обнаружения столкновений с помощью arcade. Вы можете использовать один из трех различных методов Sprite для быстрого обнаружения столкновений:

  1. Sprite.collides_with_point((x,y)) возвращает True, если заданная точка (x,y) находится в пределах границы текущего спрайта, и False в противном случае.
  2. Sprite.collides_with_sprite(Sprite) возвращает True, если данный спрайт перекрывается с текущим спрайтом, и False в противном случае.
  3. Sprite.collides_with_list(SpriteList) возвращает список, содержащий все спрайты в SpriteList, которые перекрываются с текущим спрайтом. Если нет перекрывающихся спрайтов, то список будет пустым, то есть его длина будет равна нулю.

Поскольку вас интересует, столкнулся ли однопользовательский спрайт с каким-либо из вражеских спрайтов, последний метод - это именно то, что вам нужно. Вы вызываете self.player.collides_with_list(self.enemies_list) и проверяете, содержит ли возвращаемый список какие-либо спрайты. Если это так, то вы заканчиваете игру.

Итак, куда вы сделаете этот звонок? Лучше всего это сделать в .on_update(), непосредственно перед тем, как вы обновите позиции всего:

189def on_update(self, delta_time: float):
190    """Update the positions and statuses of all game objects
191    If paused, do nothing
192
193    Arguments:
194        delta_time {float} -- Time since the last update
195    """
196
197    # If paused, don't update anything
198    if self.paused:
199        return
200
201    # Did you hit anything? If so, end the game
202    if self.player.collides_with_list(self.enemies_list):
203        arcade.close_window()
204
205    # Update everything
206    self.all_sprites.update()

Строки 202 и 203 проверяют наличие коллизии между player и любым спрайтом в .enemies_list. Если возвращаемый список содержит какие-либо спрайты, это указывает на столкновение, и вы можете завершить игру. Итак, зачем вам проверять перед обновлением позиций всего? Запомните последовательность действий в игровом цикле Python:

  1. Вы обновляете состояния игровых объектов. Вы делаете это в .on_update().
  2. Вы рисуете все игровые объекты на их новых местах. Вы делаете это в .on_draw().

Если вы проверите наличие коллизий после обновления всего в .on_update(), то при обнаружении коллизии никакие новые позиции отображаться не будут. На самом деле вы проверяете наличие коллизий на основе позиций спрайтов, которые еще не были показаны пользователю. Игроку может показаться, что игра закончилась до того, как произошло реальное столкновение! При первой проверке убедитесь, что то, что видно игроку, совпадает с состоянием игры, которое вы проверяете.

Теперь у вас есть игра на Python, которая выглядит привлекательно и представляет собой сложную задачу! Теперь вы можете добавить несколько дополнительных функций, которые помогут выделить вашу игру на Python.

Дополнительные услуги

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

Звук

Звук - важная составляющая любой компьютерной игры. Без звука ваша игра на Python - от взрывов и вражеских насмешек до фоновой музыки - выглядит немного скучноватой. Изначально arcade обеспечивает поддержку файлов формата WAV. Если установлена и доступна библиотека ffmpeg, то arcade также поддерживает Ogg и MP3 форматирование файлов. Вы добавите три различных звуковых эффекта и немного фоновой музыки:

  1. Первый звуковой эффект воспроизводится по мере продвижения игрока вверх.
  2. Второй звуковой эффект воспроизводится, когда игрок перемещается вниз.
  3. Третий звуковой эффект воспроизводится при столкновении.
  4. Фоновая музыка - это последнее, что вы добавите.

Вы начнете со звуковых эффектов.

Звуковые эффекты

Прежде чем вы сможете воспроизводить любой из этих звуков, вы должны загрузить их. Вы делаете это в .setup():

66# Spawn a new enemy every 0.25 seconds
67arcade.schedule(self.add_enemy, 0.25)
68
69# Spawn a new cloud every second
70arcade.schedule(self.add_cloud, 1.0)
71
72# Load your sounds
73# Sound sources: Jon Fincher
74self.collision_sound = arcade.load_sound("sounds/Collision.wav")
75self.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav")
76self.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")

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

Когда звуки будут загружены, вы сможете воспроизвести их в подходящее время. Для .move_up_sound и .move_down_sound это происходит во время .on_key_press() обработчика:

134def on_key_press(self, symbol, modifiers):
135    """Handle user keyboard input
136    Q: Quit the game
137    P: Pause the game
138    I/J/K/L: Move Up, Left, Down, Right
139    Arrows: Move Up, Left, Down, Right
140
141    Arguments:
142        symbol {int} -- Which key was pressed
143        modifiers {int} -- Which modifiers were pressed
144    """
145    if symbol == arcade.key.Q:
146        # Quit immediately
147        arcade.close_window()
148
149    if symbol == arcade.key.P:
150        self.paused = not self.paused
151
152    if symbol == arcade.key.I or symbol == arcade.key.UP:
153        self.player.change_y = 5
154        arcade.play_sound(self.move_up_sound)
155
156    if symbol == arcade.key.K or symbol == arcade.key.DOWN:
157        self.player.change_y = -5
158        arcade.play_sound(self.move_down_sound)

Теперь всякий раз, когда игрок перемещается вверх или вниз, в вашей игре на Python будет воспроизводиться звук.

Звук столкновения будет воспроизводиться всякий раз, когда .on_update() обнаружит столкновение:

def on_update(self, delta_time: float):
    """Update the positions and statuses of all game objects
    If paused, do nothing

    Arguments:
        delta_time {float} -- Time since the last update
    """

    # If paused, don't update anything
    if self.paused:
        return

    # Did you hit anything? If so, end the game
    if len(self.player.collides_with_list(self.enemies_list)) > 0:
        arcade.play_sound(self.collision_sound)
        arcade.close_window()

    # Update everything
    self.all_sprites.update()

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

Фоновая музыка

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

66# Spawn a new enemy every 0.25 seconds
67arcade.schedule(self.add_enemy, 0.25)
68
69# Spawn a new cloud every second
70arcade.schedule(self.add_cloud, 1.0)
71
72# Load your background music
73# Sound source: http://ccmixter.org/files/Apoxode/59262
74# License: https://creativecommons.org/licenses/by/3.0/
75self.background_music = arcade.load_sound(
76    "sounds/Apoxode_-_Electric_1.wav"
77)
78
79# Load your sounds
80# Sound sources: Jon Fincher
81self.collision_sound = arcade.load_sound("sounds/Collision.wav")
82self.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav")
83self.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")
84
85# Start the background music
86arcade.play_sound(self.background_music)

Теперь у вас есть не только звуковые эффекты, но и отличная фоновая музыка!

Ограничения по звуку

Существуют некоторые ограничения на то, что arcade в настоящее время может делать со звуком:

  1. Нет регулировки громкости для любых звуков.
  2. Невозможно повторить звук, например, зацикливание фоновой музыки.
  3. Невозможно определить, воспроизводится ли звук в данный момент, прежде чем пытаться остановить его.
  4. Без ffmpeg вы ограничены звуками в формате WAV, которые могут быть большими.

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

Скорость игры на Python

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

Частота кадров в arcade игре на Python регулируется игровым циклом в arcade.run(). Игровой цикл Python вызывает .on_update() и .on_draw() примерно 60 раз в секунду. Таким образом, частота кадров в игре составляет 60 кадров в секунду или 60 кадров в секунду.

Обратите внимание, что в приведенном выше описании указано, что частота кадров составляет примерно 60 кадров в секунду. Точность этой частоты не гарантируется. Оно может увеличиваться или уменьшаться в зависимости от многих факторов, таких как нагрузка на компьютер или более длительное, чем обычно, время обновления. Как программист игр на Python, вы хотите убедиться, что ваша игра на Python работает одинаково, независимо от того, работает ли она со скоростью 60 кадров в секунду, 30 кадров в секунду или с любой другой скоростью. Итак, как вы это делаете?

Перемещение по времени

Представьте себе объект, движущийся в пространстве со скоростью 60 километров в минуту. Вы можете рассчитать, какое расстояние объект пролетит за любой промежуток времени, умножив это время на скорость объекта:

Calculating distance based on speed and time.

Объект перемещается на 120 километров за 2 минуты и на 30 километров за полминуты.

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

Напомним, что .on_update() принимает единственный параметр, delta_time. Это количество времени в секундах, прошедшее с момента последнего вызова .on_update(). Для игры, работающей со скоростью 60 кадров в секунду, delta_time будет составлять 1/60 секунды, или примерно 0,0167 секунды. Если вы умножите прошедшее время на количество перемещений спрайта, то убедитесь, что перемещение спрайта основано на прошедшем времени, а не на частоте кадров.

Обновление перемещения спрайта

Есть только одна проблема — ни Sprite.on_update(), ни SpriteList.on_update() не принимают параметр delta_time. Это означает, что нет способа передать его вашим спрайтам для автоматической обработки. Следовательно, чтобы реализовать эту функцию, вам необходимо обновить позиции ваших спрайтов вручную. Замените вызов self.all_sprites.update() на .on_update() следующим кодом:

def on_update(self, delta_time: float):
    """Update the positions and statuses of all game objects
    If paused, do nothing

    Arguments:
        delta_time {float} -- Time since the last update
    """

    # If paused, don't update anything
    if self.paused:
        return

    # Did you hit anything? If so, end the game
    if len(self.player.collides_with_list(self.enemies_list)) > 0:
        arcade.play_sound(self.collision_sound)
        arcade.close_window()

    # Update everything
    for sprite in self.all_sprites:
        sprite.center_x = int(
            sprite.center_x + sprite.change_x * delta_time
        )
        sprite.center_y = int(
            sprite.center_y + sprite.change_y * delta_time
        )

В этом новом коде вы изменяете положение каждого спрайта вручную, умножая .change_x и .change_y на delta_time. Это гарантирует, что спрайт перемещается на постоянное расстояние каждую секунду, а не на постоянное расстояние в каждом кадре, что может сгладить игровой процесс.

Обновление параметров спрайта

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

 93def add_enemy(self, delta_time: float):
 94    """Adds a new enemy to the screen
 95
 96    Arguments:
 97        delta_time {float} -- How much time as passed since the last call
 98    """
 99
100    # First, create the new enemy sprite
101    enemy = FlyingSprite("images/missile.png", SCALING)
102
103    # Set its position to a random height and off screen right
104    enemy.left = random.randint(self.width, self.width + 80)
105    enemy.top = random.randint(10, self.height - 10)
106
107    # Set its speed to a random speed heading left
108    enemy.velocity = (random.randint(-20, -5), 0)

Благодаря новым расчетам движения, основанным на времени, ваши враги теперь будут перемещаться с максимальной скоростью 20 пикселей в секунду. Это означает, что в окне шириной 800 пикселей самому быстрому врагу потребуется сорок секунд, чтобы пересечь экран. Далее, если враг появится в восьмидесяти пикселях справа от окна, то самому быстрому потребуется целых четыре секунды, чтобы просто появиться!

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

Настройки и усовершенствования

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

  1. Когда игра приостановлена, враги и облака по-прежнему генерируются с помощью запланированных функций. Это означает, что, когда игра не приостановлена, вас поджидает огромная волна игроков. Как вы можете предотвратить это?
  2. Как упоминалось выше, из-за некоторых ограничений звукового движка arcade фоновая музыка не повторяется. Как вы решаете эту проблему?
  3. Когда игрок сталкивается с противником, игра внезапно заканчивается без воспроизведения звука столкновения. Как сохранить игру открытой в течение секунды или двух, прежде чем окно закроется?

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

Примечание к источникам

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

Вот несколько источников информации о музыке, звуке и искусстве, в которых вы можете найти полезный контент:

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

Заключение

Компьютерные игры - отличное введение в программирование, а библиотека arcade - отличный первый шаг. Разработанная как современный фреймворк на Python для создания игр, вы можете создавать захватывающие игры на Python с великолепной графикой и звуком.

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

  • Установите библиотеку arcade
  • Рисуйте элементы на экране
  • Работа с arcade игровым циклом Python
  • Управление графическими элементами на экране
  • Обработка пользовательского ввода
  • Воспроизведение звуковых эффектов и музыки
  • Опишите, чем программирование игр на Python в arcade отличается от pygame

Я надеюсь, что вы дадите arcade попробовать. Если да, то, пожалуйста, оставьте комментарий ниже, и удачи в работе с Python! Вы можете скачать все материалы, использованные в этом руководстве, по ссылке ниже:

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

Back to Top