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

Добавление случайно движущихся объектов в игру на Python Arcade

5 min read Game Dev Обновлено 19 Dec 2025
Случайные движущиеся объекты в Python Arcade
Случайные движущиеся объекты в Python Arcade

Мальчик играет в компьютерную игру с наушниками и контроллером

Введение

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

Этот материал шаг за шагом объясняет:

  • как создать простое окно Arcade и отрисовать игрока;
  • как добавлять множество объектов и заставлять их двигаться случайно;
  • как направлять объекты к игроку и включать их только в зоне досягаемости;
  • как обнаруживать столкновения и балансировать механику спавна и скорости.

Важно: примеры используют библиотеку arcade; установите её командой pip install arcade.

Что понадобится

  • Python 3.7+;
  • установленный пакет arcade (pip install arcade);
  • базовые знания Python (классы, списки, циклы);
  • опционально: GitHub-репозиторий с кодом (авторский код под MIT в исходном материале).

Основные понятия (в одну строку)

  • Sprite — объект для отрисовки и коллизий (здесь мы используем простые круги);
  • delta_time — время между кадрами; полезно для согласованного движения;
  • нормализация вектора — представление направления с длиной 1 для стабильной скорости.

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

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

import arcade

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
PLAYER_RADIUS = 15

class MyGame(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)

        self.player_x = SCREEN_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 update(self, delta_time):
        pass

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

if __name__ == "__main__":
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
    arcade.run()

Совет: для плавного движения умножайте смещение на delta_time и используйте флаги клавиш (on_key_release / on_key_press) вместо мгновенных прыжков по 5 пикселей.

Добавление нескольких объектов

Создаём список объектов с случайными координатами и рисуем их как красные круги.

import arcade
import random

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
PLAYER_RADIUS = 15
OBJECT_RADIUS = 10
NUM_OBJECTS = 10

class MyGame(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)

        self.player_x = SCREEN_WIDTH // 2
        self.player_y = PLAYER_RADIUS + 10

        self.objects = []
        for _ in range(NUM_OBJECTS):
            x = random.randint(0, SCREEN_WIDTH)
            y = random.randint(0, SCREEN_HEIGHT)
            self.objects.append((x, y))

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

        for obj in self.objects:
            x, y = obj
            arcade.draw_circle_filled(x, y, OBJECT_RADIUS, arcade.color.RED)

    def update(self, delta_time):
        pass

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

if __name__ == "__main__":
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
    arcade.run()

Несколько объектов, размещённых случайно, и игрок в Arcade

Реализация случайного движения

Чтобы объекты перемещались случайно, в методе update генерируйте случайный dx и dy и прибавляйте к текущим координатам.

def update(self, delta_time):
    for i in range(len(self.objects)):
        x, y = self.objects[i]
        dx = random.randint(-5, 5)
        dy = random.randint(-5, 5)
        x += dx
        y += dy
        self.objects[i] = (x, y)

Такой подход прост, но может привести к резким скачкам и выходу объектов за границы экрана — обработайте границы (clamp) или отталкивание.

Случайно движущиеся объекты вместе с игроком в Arcade

Движение объектов навстречу игроку

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

import math

def update(self, delta_time):
    for i in range(len(self.objects)):
        x, y = self.objects[i]
        dx = self.player_x - x
        dy = self.player_y - y
        distance = math.sqrt(dx  2 + dy  2)
        if distance == 0:
            continue
        dx /= distance
        dy /= distance
        x += dx * 3
        y += dy * 3
        self.objects[i] = (x, y)

Учтите деление на ноль — если distance == 0, пропустите обновление или обработайте столкновение.

Объекты движутся к игроку в Arcade

Активация объектов в зоне вокруг игрока

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

def update(self, delta_time):
    for i in range(len(self.objects)):
        x, y = self.objects[i]
        dx = self.player_x - x
        dy = self.player_y - y
        distance = math.sqrt(dx  2 + dy  2)

        if distance < 100:  # радиус активации
            if distance == 0:
                continue
            dx /= distance
            dy /= distance
            x += dx * 3
            y += dy * 3
            self.objects[i] = (x, y)

Датчики столкновений и взаимодействие

Добавим обработку столкновений: при столкновении удаляем объект и создаём новый в случайной позиции.

def update(self, delta_time):
    for i in range(len(self.objects)-1, -1, -1):  # идти в обратном порядке при удалении
        x, y = self.objects[i]
        dx = self.player_x - x
        dy = self.player_y - y
        distance = math.sqrt(dx  2 + dy  2)

        if distance < PLAYER_RADIUS + OBJECT_RADIUS:
            # столкновение: удаляем и сразу спавним новый объект
            self.objects.pop(i)
            self.objects.append((random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT)))
        elif distance < 100:
            dx /= distance
            dy /= distance
            x += dx * 3
            y += dy * 3
            self.objects[i] = (x, y)

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

Балансировка случайности

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

Ограничение максимальной скорости

Чтобы объекты не прыгали слишком быстро, используйте MAX_SPEED и clamp для компонентов скорости.

MAX_SPEED = 5

# внутри update
speed = 3
vx = dx * speed
vy = dy * speed
vx = max(-MAX_SPEED, min(MAX_SPEED, vx))
vy = max(-MAX_SPEED, min(MAX_SPEED, vy))

x += vx
y += vy

Контроль частоты появления (spawn rate)

Добавьте таймер спавна, чтобы ограничить, как часто появляются новые объекты.

import time

SPAWN_DELAY = 2.0  # секунды
MAX_OBJECTS = 20

class MyGame(arcade.Window):
    def __init__(self, width, height):
        # ...
        self.last_spawn_time = time.time()

    def update(self, delta_time):
        if time.time() - self.last_spawn_time > SPAWN_DELAY:
            if len(self.objects) < MAX_OBJECTS:
                self.objects.append((random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT)))
                self.last_spawn_time = time.time()

        # обновление объектов далее

Регулируйте SPAWN_DELAY и MAX_OBJECTS, чтобы снизить или повысить сложность.

Полный пример: объединённый шаблон

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

import arcade
import random
import math
import time

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
PLAYER_RADIUS = 15
OBJECT_RADIUS = 10
MAX_SPEED = 4
SPAWN_DELAY = 1.5
MAX_OBJECTS = 15

class MyGame(arcade.Window):
    def __init__(self, width, height):
        super().__init__(width, height)
        arcade.set_background_color(arcade.color.WHITE)
        self.player_x = SCREEN_WIDTH // 2
        self.player_y = PLAYER_RADIUS + 10
        self.keys = set()
        self.objects = []
        for _ in range(8):
            self.objects.append((random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT)))
        self.last_spawn_time = time.time()

    def on_draw(self):
        arcade.start_render()
        arcade.draw_circle_filled(self.player_x, self.player_y, PLAYER_RADIUS, arcade.color.BLUE)
        for x, y in self.objects:
            arcade.draw_circle_filled(x, y, OBJECT_RADIUS, arcade.color.RED)

    def on_key_press(self, key, modifiers):
        self.keys.add(key)

    def on_key_release(self, key, modifiers):
        self.keys.discard(key)

    def update(self, delta_time):
        # движение игрока
        if arcade.key.LEFT in self.keys:
            self.player_x -= 200 * delta_time
        if arcade.key.RIGHT in self.keys:
            self.player_x += 200 * delta_time

        # спавн
        if time.time() - self.last_spawn_time > SPAWN_DELAY and len(self.objects) < MAX_OBJECTS:
            self.objects.append((random.randint(0, SCREEN_WIDTH), random.randint(0, SCREEN_HEIGHT)))
            self.last_spawn_time = time.time()

        # обновление и коллизии (обход в обратном порядке)
        for i in range(len(self.objects) - 1, -1, -1):
            x, y = self.objects[i]
            dx = self.player_x - x
            dy = self.player_y - y
            dist = math.hypot(dx, dy)
            if dist < PLAYER_RADIUS + OBJECT_RADIUS:
                self.objects.pop(i)
                continue
            if dist < 120 and dist != 0:
                nx = dx / dist
                ny = dy / dist
                vx = max(-MAX_SPEED, min(MAX_SPEED, nx * 3))
                vy = max(-MAX_SPEED, min(MAX_SPEED, ny * 3))
                x += vx
                y += vy
                self.objects[i] = (x, y)

if __name__ == '__main__':
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
    arcade.run()

Советы по отладки и оптимизации

  • Проверяйте границы экрана и используйте clamp, чтобы объекты не уходили за пределы видимой области.
  • Для большого количества объектов переходите на arcade.Sprite и sprite lists — они оптимизированы по отрисовке и коллизиям.
  • Итерируйтесь в обратном порядке при удалении элементов из списка.
  • Используйте delta_time для детерминированного движения на разных FPS.

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

  • Использовать физический движок (pymunk) для реалистичных столкновений и отскоков.
  • Хранить состояние объектов в numpy-массиве для векторизованных обновлений (оптимизация при большом числе объектов).
  • Для визуализации и коллизий использовать sprite-систему Arcade вместо ручных кругов.

Когда такой подход не подходит

  • Когда требуется точная физика со столкновениями и импульсами — лучше интегрировать сторонний физический движок.
  • Когда объектов очень много (> тысячи) — нужен другой подход к хранению и обновлению (векторизация, spatial partitioning).

Быстрая методика (mini-methodology)

  1. Начните с простого списка координат и визуализации.
  2. Добавьте нормализацию направления для контролируемой скорости.
  3. Введите ограничения скорости и радиусы активации.
  4. Перейдите на Sprite-листы при проблемах с производительностью.
  5. Тестируйте границы и экстремальные случаи (distance=0, пустые списки).

Чек-лист для разработчика

  • Установлен arcade
  • Игрок плавно двигается с delta_time
  • Объекты корректно спавнятся и не выходят за экран
  • Коллизии обрабатываются без ошибок при удалении
  • Ограничение скорости не позволяет «телепортам»
  • Пределы SPAWN_DELAY и MAX_OBJECTS настроены под желаемую сложность

Тест-кейсы и критерии приёмки

  • При столкновении объект исчезает и создаётся новый (или счёт увеличивается).
  • Объекты в зоне активации двигаются к игроку, вне зоны — стоят на месте.
  • При увеличении NUM_OBJECTS и SPAWN_DELAY игра остаётся отзывчивой.
  • Нет ошибок деления на ноль при совпадении координат.

Короткие советы по UX и игровому дизайну

  • Смешивайте поведение: 60% объектов — пассивные, 40% — агрессивные.
  • Используйте разные скорости и радиусы активации для разнообразия.
  • Дайте игроку инструменты (ускорение, щит), чтобы взаимодействие было значимым.

Important: всегда профилируйте и проверяйте производительность на целевых устройствах.

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

Добавление случайно движущихся объектов в игру с помощью Arcade — простой путь сделать игру динамичнее. Начните с базовых списков координат, затем добавляйте нормализацию, контроль скорости, обработку столкновений и ограничение спавна. При увеличении масштаба переходите на оптимизированные структуры данных и системные подходы.

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

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

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

Как смотреть HBO из Европы — VPN и подарочные карты
Стриминг

Как смотреть HBO из Европы — VPN и подарочные карты

Удаление аккаунта SoundCloud — инструкция
Музыка

Удаление аккаунта SoundCloud — инструкция

Лучшие приложения для отслеживания сериалов
Entertainment

Лучшие приложения для отслеживания сериалов

Подключение Xbox One контроллера к Mac
Гайды

Подключение Xbox One контроллера к Mac

Где смотреть «Дом дракона»
Стриминг

Где смотреть «Дом дракона»

Переадресация звонков в Google Voice — настройка
Связь

Переадресация звонков в Google Voice — настройка