- PVSM.RU - https://www.pvsm.ru -

Создаем 2D игру на Python с библиотекой Arcade

Всем привет!

Мы продолжаем делится с вами интересными найденными вещами про питончик. Сегодня вот решили разобраться с 2D играми. Это, конечно, немного попроще, чем то, что проходят у нас на курсе «Разработчик Python» [1], но не менее интересно это уж точно.

Поехали.

Python — выдающийся язык для начинающих изучать программирование. Он также идеально подходит тем, кто хочет “просто взять и сделать”, а не тратить кучу времени на шаблонный код. Arcade [2] — библиотека Python для создания 2D игр, с низким порогом вхождения, но очень функциональная в опытных руках. В этом статье я объясню, как начать использовать Python и Arcade для программирования игр.

Я начал разрабатывать на Arcade после преподавания азов библиотеки PyGame [3]студентам. Я очно преподавал PyGames в течение почти 10 лет, а также разработал ProgramArcadeGames.com [4] для обучения онлайн. PyGames отличная, но в какой-то момент я понял, что устал тратить время на оправдание багов, которые никогда не фиксятся [5].

Меня беспокоило преподавание таких вещей, как событийный цикл [6], которым уже почти не пользовались. И был целый раздел [7], в котором я объяснял, почему y-координаты повернуты в противоположном направлении. PyGames обновлялась редко и базировалась на старой библиотеке SDL 1 [8], а не чем-то более современном вроде OpenGL [9]. На светлое будущее я не рассчитывал.

В моих мечтах была простая и мощная библиотека, которая бы использовала новые фичи Python 3, например, декораторы и тайп-хинтинг. Ей оказалась Arcade. Посмотрим, как начать ее использовать.

Создаем 2D игру на Python с библиотекой Arcade - 1

Установка

Arcade, как и многие другие пакеты, доступна на PyPi [10], а значит, можно установить Arcade при помощи команды pip (или pipenv [11]). Если Python уже установлен, скорее всего можно просто открыть командную строку Windows и написать:

pip install arcade

А в Linux и MacOS:

pip3 install arcade

Для более детализированной инструкции по установке, почитайте документацию [12]по установке Arcade.

Простой рисунок

Вы можете открыть окно и нарисовать простой рисунок всего несколькими строчками кода. В качестве примера, нарисуем смайлик, как на картинке ниже:

Создаем 2D игру на Python с библиотекой Arcade - 2

Скрипт ниже показывает, как это сделать, используя команды [13]рисования Arcade. Заметьте, что вам не обязательно знать, как использовать классы или определять функции. Программирование с быстрым визуальным фидбеком — хороший старт для тех, кто только учится.

import arcade

# Задать константы для размеров экрана
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 600

# Открыть окно. Задать заголовок и размеры окна (ширина и высота)
arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Drawing Example")

# Задать белый цвет фона.
# Для просмотра списка названий цветов прочитайте:
# http://arcade.academy/arcade.color.html
# Цвета также можно задавать в (красный, зеленый, синий) и
# (красный, зеленый, синий, альфа) формате.
arcade.set_background_color(arcade.color.WHITE)

# Начать процесс рендера. Это нужно сделать до команд рисования
arcade.start_render()

# Нарисовать лицо
x = 300
y = 300
radius = 200
arcade.draw_circle_filled(x, y, radius, arcade.color.YELLOW)

# Нарисовать правый глаз
x = 370
y = 350
radius = 20
arcade.draw_circle_filled(x, y, radius, arcade.color.BLACK)

# Нарисовать левый глаз
x = 230
y = 350
radius = 20
arcade.draw_circle_filled(x, y, radius, arcade.color.BLACK)

# Нарисовать улыбку
x = 300
y = 280
width = 120
height = 100
start_angle = 190
end_angle = 350
arcade.draw_arc_outline(x, y, width, height, arcade.color.BLACK, start_angle, 
                        end_angle, 10)

# Завершить рисование и показать результат
arcade.finish_render()

# Держать окно открытым до тех пор, пока пользователь не нажмет кнопку “закрыть”
arcade.run()

Использование функций

Конечно, писать код в глобальном контексте — не лучший способ. К счастью, использование функций поможет улучшить ваш код. Ниже приведен пример того, как нарисовать елку в заданных координатах (x, y), используя функцию:

def draw_pine_tree(x, y):
    """ Эта функция рисует елку в указанном месте"""
    
    # Нарисовать треугольник поверх ствола.
    # Необходимы три x, y точки для рисования треугольника.
    arcade.draw_triangle_filled(x + 40, y,       # Point 1
                                x, y - 100,      # Point 2
                                x + 80, y - 100, # Point 3
                                arcade.color.DARK_GREEN)

    # Нарисовать ствол
    arcade.draw_lrtb_rectangle_filled(x + 30, x + 50, y - 100, y - 140,
                                      arcade.color.DARK_BROWN)

Для полного примера, посмотрите рисунок с функциями [14].

Создаем 2D игру на Python с библиотекой Arcade - 3

Более опытные программисты знают, что современные программы сначала загружают графическую информацию на видеокарту, а затем просят ее отрисовать batch-файлом. Arcade это поддерживае [15]т. Индивидуальная отрисовка 10000 прямоугольников занимает 0.8 секунды. Отрисовка того же количества батником займет менее 0.001 секунды.

Класс Window

Большие программы обычно базируются на классе Window [16]или используют декораторы [17]. Это позволяет программисту писать код, контролирующий отрисовку, обновление и обработку входных данных пользователя. Ниже приведен шаблон для программы с Window-основой.

import arcade

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600


class MyGame(arcade.Window):
    """ Главный класс приложения. """

    def __init__(self, width, height):
        super().__init__(width, height)

        arcade.set_background_color(arcade.color.AMAZON)

    def setup(self):
        # Настроить игру здесь
        pass

    def on_draw(self):
        """ Отрендерить этот экран. """
        arcade.start_render()
        # Здесь код рисунка

    def update(self, delta_time):
        """ Здесь вся игровая логика и логика перемещения."""
        pass


def main():
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
    game.setup()
    arcade.run()


if __name__ == "__main__":
    main() 

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

  • on_draw: Весь код для отрисовки экрана находится здесь.
  • update: Весь код для перемещения объектов и отработки игровой логики находится здесь. Вызывается примерно 60 раз в секунду.
  • on_key_press: Обрабатывает события при нажатии кнопки, например, движение персонажа.
  • on_key_release: Обрабатывает события при отпускании кнопки, например, остановка персонажа.
  • on_mouse_motion: Вызывается каждый раз при движении мышки.
  • on_mouse_press: Вызывается при нажатии кнопки мыши.
  • set_viewport: Эта функция используется в скроллерах, когда мир значительно больше, чем то что видно на одном экране. Вызов set_viewport позволяет программисту задать ту часть экрана, которая будет видна.

Спрайты

Спрайты [18] — простой способ создания 2D bitmap объектов в Arcade. В нем есть методы, позволяющие с легкостью рисовать, перемещать и анимировать спрайты. Также можно использовать спрайты для отслеживания коллизий между объектами.

Создание спрайта

Создать инстанс Sprite [19]класса Arcade очень легко. Программисту необходимо только название файла изображения, на котором будет основываться спрайт, и, опционально, число раз для увеличения или уменьшения изображения. Например:

SPRITE_SCALING_COIN = 0.2

coin = arcade.Sprite("coin_01.png", SPRITE_SCALING_COIN)

Этот код создает спрайт, используя изображение coin_01.png. Картинка уменьшится на 20% по сравнении с шириной и высотой оригинала.

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

Спрайты обычно организуются в списки. Они помогают упростить их управление. Спрайты в списке будут использовать OpenGl для групповой batch-отрисовки. Нижеприведенный код настраивает игру, где есть игрок и множество монет, которые игрок должен собрать. Мы используем два списка — один для игрока и один для монеток.

def setup(self):
    """ Настроить игру и инициализировать переменные. """

    # Создать список спрайтов
    self.player_list = arcade.SpriteList()
    self.coin_list = arcade.SpriteList()

    # Счет
    self.score = 0

    # Задать игрока и
    # Его изображение из kenney.nl
    self.player_sprite = arcade.Sprite("images/character.png", 
                                       SPRITE_SCALING_PLAYER)
    self.player_sprite.center_x = 50 # Стартовая позиция
    self.player_sprite.center_y = 50
    self.player_list.append(self.player_sprite)

    # Создать монетки
    for i in range(COIN_COUNT):

        # Создать инстанс монеток
        # и их изображение из kenney.nl
        coin = arcade.Sprite("images/coin_01.png", SPRITE_SCALING_COIN)

        # Задать положение монеток
        coin.center_x = random.randrange(SCREEN_WIDTH)
        coin.center_y = random.randrange(SCREEN_HEIGHT)

        # Добавить монетку к списку 
        self.coin_list.append(coin)

Мы с легкостью можем отрисовать все монетки в списке монеток:

def on_draw(self):
    """ Нарисовать все """
    arcade.start_render()
    self.coin_list.draw()
    self.player_list.draw()

Отслеживание коллизий спрайтов

Функция check_for_collision_with_list позволяет увидеть, если спрайт наталкивается на другой спрайт из списка. Используем ее, чтобы увидеть все монетки, с которыми пересекается спрайт игрока. Применив простой for- цикл, можно избавиться от монетки в игре и увеличить счет.

def update(self, delta_time):
    # Сгенерировать список всех спрайтов монеток, которые пересекаются с игроком.
    coins_hit_list = arcade.check_for_collision_with_list(self.player_sprite, 
                                                          self.coin_list)

    # Пройтись циклом через все пересекаемые спрайты, удаляя их и увеличивая счет.
    for coin in coins_hit_list:
        coin.kill()
        self.score += 1

С полным примером можно ознакомиться в collect_coins.py [20].

Игровая физика

Во многих играх есть физика в том или ином виде. Самые простое, например, что top-down игры не позволяют игроку проходить сквозь стены. Платформеры добавляют сложности с гравитацией и движущимися платформами. Некоторые игры используют полноценные физические 2D движки с массами, трением, пружинами и тд.

Top-down игры

Создаем 2D игру на Python с библиотекой Arcade - 4

Для простых игр с видом сверху программе на Arcade необходим список стен (или чего-то подобного), через которые игрок не сможет проходить. Обычно я называю это wall_list. Затем создается физический движок в установочном коде класса Window:

self.physics_engine = arcade.PhysicsEngineSimple(self.player_sprite, self.wall_list)

player_sprite получает вектор движения с двумя атрибутами change_x и change_y. Просто пример использования — перемещение игрока с помощью клавиатуры.

MOVEMENT_SPEED = 5

def on_key_press(self, key, modifiers):
    """Вызывается при нажатии пользователем клавиши"""

    if key == arcade.key.UP:
        self.player_sprite.change_y = MOVEMENT_SPEED
    elif key == arcade.key.DOWN:
        self.player_sprite.change_y = -MOVEMENT_SPEED
    elif key == arcade.key.LEFT:
        self.player_sprite.change_x = -MOVEMENT_SPEED
    elif key == arcade.key.RIGHT:
        self.player_sprite.change_x = MOVEMENT_SPEED

def on_key_release(self, key, modifiers):
    """Вызывается, когда пользователь отпускает клавишу"""

    if key == arcade.key.UP or key == arcade.key.DOWN:
        self.player_sprite.change_y = 0
    elif key == arcade.key.LEFT or key == arcade.key.RIGHT:
        self.player_sprite.change_x = 0

Несмотря на то что этот код задает скорость игрока, он его не перемещает. Метод update в классе Window вызывает physics_engine.update(), что заставит игрока двигаться, но не через стены.

def update(self, delta_time):
    """ Передвижение и игровая логика """

     self.physics_engine.update()

Пример полностью можно посмотреть в sprite_move_walls.py [21].

Платформеры

Создаем 2D игру на Python с библиотекой Arcade - 5

Переход к платформеру с видом сбоку достаточно прост. Программисту необходимо переключить физический движок на PhysicsEnginePlatformer и добавить гравитационную константу.

self.physics_engine = arcade.PhysicsEnginePlatformer(self.player_sprite,
                                                     self.wall_list, 
                                                     gravity_constant=GRAVITY)

Для добавления тайлов и блоков, из которых будет состоять уровень, можно использовать программу вроде Tiled [22].

Пример доступен в sprite_tiled_map.py. [23]

Учитесь на примере

Учиться на примере — один из лучших методов. В библиотеке Arcade есть большой список образцов программ [24], на которые можно ориентироваться при создании игры. Эти примеры раскрывают концепты игр, о которых спрашивали мои онлайн и оффлайн студенты в течение нескольких лет.

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

python -m arcade.examples.sprite_moving_platforms

THE END

Как всегда ждём ваши комментарии и вопросы, которые можно оставить тут или зайти к Стасу [25]на день открытых дверей [26].

Автор: MaxRokatansky

Источник [27]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/288762

Ссылки в тексте:

[1] «Разработчик Python»: https://otus.pw/dNOU/

[2] Arcade: http://arcade.academy/

[3] PyGame : https://www.pygame.org/

[4] ProgramArcadeGames.com: http://programarcadegames.com/

[5] которые никогда не фиксятся: https://stackoverflow.com/questions/10148479/artifacts-when-drawing-primitives-with-pygame

[6] событийный цикл: https://www.pygame.org/docs/tut/tom_games2.html

[7] целый раздел: http://programarcadegames.com/index.php?chapter=introduction_to_graphics&lang=en#section_5_1

[8] SDL 1: https://www.libsdl.org/download-1.2.php

[9] OpenGL: https://opensource.com/article/18/4/opengl-bindings-bash

[10] PyPi: https://pypi.python.org/pypi

[11] pipenv: https://opensource.com/article/18/2/why-python-devs-should-use-pipenv

[12] документацию : http://arcade.academy/installation.html

[13] команды : http://arcade.academy/quick_index.html#drawing-module

[14] рисунок с функциями: http://arcade.academy/examples/drawing_with_functions.html

[15] Arcade это поддерживае: http://arcade.academy/examples/shape_list_demo.html

[16] Window : http://arcade.academy/arcade.html#arcade.application.Window

[17] декораторы: http://arcade.academy/examples/decorator_drawing_example.html#decorator-drawing-example

[18] Спрайты : https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D1%80%D0%B0%D0%B9%D1%82_(%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D0%BD%D0%B0%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%BA%D0%B0)

[19] Sprite : http://arcade.academy/arcade.html#arcade.sprite.Sprite

[20] collect_coins.py: http://arcade.academy/examples/sprite_collect_coins.html

[21] sprite_move_walls.py: http://arcade.academy/examples/sprite_move_walls.html

[22] Tiled: http://www.mapeditor.org/

[23] sprite_tiled_map.py.: http://arcade.academy/examples/sprite_tiled_map.html

[24] образцов программ: http://arcade.academy/examples/index.html

[25] Стасу : https://otus.pw/rr2n/

[26] день открытых дверей: https://otus.pw/fGCp/

[27] Источник: https://habr.com/post/419761/?utm_campaign=419761