Гид по технологиям

Создание врагов в играх на Python с библиотекой Arcade

6 min read Разработка игр Обновлено 22 Nov 2025
Враги в играх на Python с Arcade
Враги в играх на Python с Arcade

Стена с плакатом врага в стиле игры

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

Быстрый обзор и цели статьи

Цель этой статьи — дать практическое руководство по созданию врагов разной сложности в Arcade. Вы научитесь:

  • сделать простого игрока и статичного врага;
  • реализовать врага, преследующего игрока;
  • добавить вражеские пули и систему здоровья (HP);
  • проверить и отладить поведение врага;
  • применить лучшие практики и готовые чеклисты.

Код в примерах доступен для использования и адаптации; оформляйте его в отдельных файлах по мере экспериментов.

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

Перед началом убедитесь, что у вас установлен pip и Python. Установите Arcade одной командой:

pip install arcade

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

import arcade

# Размер окна
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Атрибуты игрока
PLAYER_RADIUS = 25
PLAYER_SPEED = 5

class GameWindow(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)
        self.player_x = width // 2
        self.player_y = PLAYER_RADIUS + 10

    def on_draw(self):
        arcade.start_render()
        arcade.draw_circle_filled(self.player_x, self.player_y, PLAYER_RADIUS, arcade.color.BLUE)

    def on_key_press(self, key, modifiers):
        if key == arcade.key.LEFT:
            self.player_x -= PLAYER_SPEED
        elif key == arcade.key.RIGHT:
            self.player_x += PLAYER_SPEED

    def update(self, delta_time):
        # Ограничение выхода за пределы экрана
        self.player_x = max(PLAYER_RADIUS, min(self.player_x, SCREEN_WIDTH - PLAYER_RADIUS))

def main():
    window = GameWindow(SCREEN_WIDTH, SCREEN_HEIGHT)
    arcade.run()

if __name__ == '__main__':
    main()

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

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

Самый простой враг — ещё один круг на экране. При столкновении с игроком можно выводить сообщение или инициировать окончание уровня.

# Добавляем в класс GameWindow

# Атрибуты врага
ENEMY_RADIUS = 20

class GameWindow(arcade.Window):
    # ... предыдущие методы ...

    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)
        self.player_x = width // 2
        self.player_y = PLAYER_RADIUS + 10
        self.enemy_x = width // 2
        self.enemy_y = height - ENEMY_RADIUS - 10
        self.enemy_radius = ENEMY_RADIUS

    def on_draw(self):
        arcade.start_render()
        arcade.draw_circle_filled(self.player_x, self.player_y, PLAYER_RADIUS, arcade.color.BLUE)
        if self.enemy_radius > 0:
            arcade.draw_circle_filled(self.enemy_x, self.enemy_y, self.enemy_radius, arcade.color.RED)

    def update(self, delta_time):
        if self.is_collision(self.player_x, self.player_y, self.enemy_x, self.enemy_y, PLAYER_RADIUS, self.enemy_radius):
            print('Game Over!')

    def is_collision(self, x1, y1, x2, y2, radius1, radius2):
        distance_squared = (x1 - x2)  2 + (y1 - y2)  2
        radius_sum_squared = (radius1 + radius2) ** 2
        return distance_squared <= radius_sum_squared

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

Враг, который преследует игрока

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

# Добавляем преследование в метод update

class GameWindow(arcade.Window):
    # ...
    def update(self, delta_time):
        if self.player_x < self.enemy_x:
            self.enemy_x -= PLAYER_SPEED
        elif self.player_x > self.enemy_x:
            self.enemy_x += PLAYER_SPEED

        # Проверка столкновения
        if self.is_collision(self.player_x, self.player_y, self.enemy_x, self.enemy_y, PLAYER_RADIUS, self.enemy_radius):
            print('Game Over!')

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

Враг преследует игрока в окне Arcade

Добавление вражеских пуль

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

class Bullet:
    def __init__(self, x, y, radius, speed):
        self.x = x
        self.y = y
        self.radius = radius
        self.speed = speed

    def update(self):
        self.y -= self.speed

class GameWindow(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)
        self.player_x = width // 2
        self.player_y = PLAYER_RADIUS + 10
        self.enemy_x = width // 2
        self.enemy_y = height - ENEMY_RADIUS - 10

        self.bullets = []
        self.bullet_radius = 5
        self.bullet_speed = 3
        self.bullet_cooldown = 60  # кадров между выстрелами
        self.bullet_timer = 0

    def on_draw(self):
        arcade.start_render()
        arcade.draw_circle_filled(self.player_x, self.player_y, PLAYER_RADIUS, arcade.color.BLUE)
        if self.enemy_radius > 0:
            arcade.draw_circle_filled(self.enemy_x, self.enemy_y, self.enemy_radius, arcade.color.RED)
        for bullet in self.bullets:
            arcade.draw_circle_filled(bullet.x, bullet.y, bullet.radius, arcade.color.BLACK)

    def update(self, delta_time):
        self.bullet_timer += 1
        if self.bullet_timer >= self.bullet_cooldown:
            self.bullets.append(Bullet(self.enemy_x, self.enemy_y - self.enemy_radius, self.bullet_radius, self.bullet_speed))
            self.bullet_timer = 0

        # Обновление пуль и проверка столкновения с игроком
        for bullet in list(self.bullets):
            bullet.update()
            if self.is_collision(self.player_x, self.player_y, bullet.x, bullet.y, PLAYER_RADIUS, bullet.radius):
                print('Попадание по игроку!')
                self.bullets.remove(bullet)
            elif bullet.y < -10:
                # Удаляем пули за пределами экрана
                self.bullets.remove(bullet)

Враг стреляет в направлении игрока

Добавление очков здоровья у врагов

HP (health points) делает врага устойчивым к нескольким попаданиям и вводит дополнительные игровые решения.

# Константы
ENEMY_HEALTH = 100

class GameWindow(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)
        self.player_x = width // 2
        self.player_y = PLAYER_RADIUS + 10
        self.enemy_x = width // 2
        self.enemy_y = height - ENEMY_RADIUS - 10
        self.enemy_health = ENEMY_HEALTH

    def on_draw(self):
        arcade.start_render()
        arcade.draw_circle_filled(self.player_x, self.player_y, PLAYER_RADIUS, arcade.color.BLUE)
        if self.enemy_health > 0:
            arcade.draw_circle_filled(self.enemy_x, self.enemy_y, ENEMY_RADIUS, arcade.color.RED)
            # Отрисовка простой полосы здоровья
            health_width = 60
            health_ratio = max(0, self.enemy_health / ENEMY_HEALTH)
            arcade.draw_rectangle_filled(self.enemy_x, self.enemy_y + ENEMY_RADIUS + 8, health_width * health_ratio, 6, arcade.color.GREEN)

    def update(self, delta_time):
        # Если игрок сталкивается с врагом, наносим урон врагу
        if self.is_collision(self.player_x, self.player_y, self.enemy_x, self.enemy_y, PLAYER_RADIUS, ENEMY_RADIUS):
            self.enemy_health -= 10
            print('Здоровье врага:', self.enemy_health)
            if self.enemy_health <= 0:
                print('Враг повержен')

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

Лучшие практики при создании врагов

Разнообразие атрибутов

Создавайте типы врагов с разными скоростями, размерами, здоровьем и силой атаки. Это позволяет формировать уровни разной сложности и вариативность игровой механики.

Уникальное поведение

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

Использование спрайтов

Перейдите на arcade.Sprite и SpriteList при масштабировании проекта: они оптимизированы по производительности и интегрируют методы столкновений.

Отслеживание состояния

Держите в объекте врага минимальный набор полей: позиция, скорость, HP, состояние (патруль/преследование/атака), таймеры для задержек.

Когда такие подходы не подходят (контрпример)

  • Если у вас 1000+ активных врагов на экране, примитивные круги и Python-циклы могут не тянуть — нужна оптимизация через спрайты, групповую обработку и, возможно, C-расширения.
  • Если требуется сложный ИИ (поиск пути, навигационные сетки), простая логика перемещения по направлению к игроку будет недостаточна.

Альтернативные подходы

  • Использовать arcade.PhysicsEngineSimple/Platformer для управления столкновениями и гравитацией.
  • Хранить поведение врагов в отдельном слое данных (компонентный подход) для облегчения тестирования.
  • Прототипировать логику в визуальных инструментах (Tiled) и загружать уровни с уже заданными паттернами.

Проверки, тесты и критерии приёмки

Критерии приёмки для базового уровня с врагами:

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

Тестовые случаи:

  1. Игрок при контакте с врагом вызывает вывод ‘Game Over!’.
  2. Враг преследует игрока по горизонтали при его перемещении.
  3. При длительном времени пули не накапливаются бесконечно (удаление при выходе за пределы).
  4. HP достигает нуля — враг исчезает.

Роль‑ориентированные чеклисты

Разработчик:

  • Код разделён по файлам: main, enemy, bullets, ui.
  • Используются спрайты и группы для большого числа врагов.
  • Есть простой API для создания новых типов врагов.

Дизайнер уровней:

  • Для каждого врага задана документация по параметрам (скорость, HP, поведение).
  • Балансировка сложности проверена на 3 типичных уровнях.

Тестировщик:

  • Есть набор автоматических тестов на столкновения и поведение пуль.
  • Подготовлены ручные сценарии воспроизведения багов (lag, overlap, multiple hits).

Мини‑методология для внедрения врагов в проект

  1. Прототип: используйте круги для быстрой проверки механики. 2. Миграция: замените круги на arcade.Sprite и SpriteList. 3. Оптимизация: профилируйте обновления, сокращайте переборы списков. 4. Баланс: настраивайте HP, силу и частоту выстрелов в зависимости от уровня.

Простая модель принятия решений (Mermaid)

flowchart TD
  A[Игрок обнаружил врага?] -->|Да| B{Дистанция < атака}
  B -->|Да| C[Атаковать]
  B -->|Нет| D{Дистанция < преследование}
  D -->|Да| E[Преследовать]
  D -->|Нет| F[Патрулировать]
  A -->|Нет| F

Советы по совместимости и миграции

  • Тестируйте проект на версии Python, которую вы используете в продакшне (рекомендуется Python 3.8 или новее).
  • Проверяйте совместимость версии Arcade в среде разработки и на целевых платформах.
  • При переходе на спрайты убедитесь, что все размеры и anchor точки корректно настроены.

Риски и простые митигаторы

  • Производительность при большом числе объектов — используйте SpriteList и ограничивайте частоту обновлений.
  • Сложный ИИ — вводите поведение постепенно и покрывайте модульными тестами.
  • Неправильные столкновения из‑за некорректных радиусов — логируйте и визуализируйте bounding box для дебага.

Краткое резюме

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

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

Полезные заметки

  • Fact box с ключевыми константами, использованными в примерах:

    • SCREEN_WIDTH = 800, SCREEN_HEIGHT = 600
    • PLAYER_RADIUS = 25, PLAYER_SPEED = 5
    • ENEMY_RADIUS = 20, ENEMY_HEALTH = 100
    • Bullet: radius = 5, speed = 3, cooldown = 60 кадров
  • Небольшой чек для быстрого дебага: при проблемах с коллизией временно рисуйте линии между центрами и выводите расстояние в лог.

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

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Не удалось инициализировать Direct3D — решения
Техподдержка

Не удалось инициализировать Direct3D — решения

Исправить Data Retrieval в Diablo 4 на Steam
Игры

Исправить Data Retrieval в Diablo 4 на Steam

Open Graph в WordPress — настройка мета‑тегов
WordPress SEO

Open Graph в WordPress — настройка мета‑тегов

getconf: адаптивные скрипты для разных Linux
Linux

getconf: адаптивные скрипты для разных Linux

Проверка входов в Windows — успешные и неудачные попытки
Безопасность Windows

Проверка входов в Windows — успешные и неудачные попытки

Исправить ошибку DistributedCOM (DCOM) в Windows
Windows

Исправить ошибку DistributedCOM (DCOM) в Windows