Добавление случайно движущихся объектов в игры на Python Arcade
Введение
Случайно движущиеся объекты добавляют в игру элемент непредсказуемости и увеличивают вовлечённость. Эта статья переводит пошаговый пример на Python с библиотекой Arcade и расширяет его практическими советами, альтернативами и проверками качества.

Требования
- Python 3.7+
- pip
- Библиотека arcade
Установка arcade:
pip install arcadeВажно: если вы собираетесь использовать работу с векторами и расстояниями, не забудьте импортировать math (или использовать numpy для больших сцен).
Быстрый старт — простая игра с одним игроком
Ниже минимальный код окна и игрока. Он создаёт окно, рисует синий круг и обеспечивает движение игрока по горизонтали.
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()Добавление нескольких объектов
Создайте список объектов, заполните его случайными позициями и отрисуйте красные кружки вокруг игрока.
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()Ниже — пример вывода изображения с рандомным размещением объектов.
Алгоритм случайного движения
Чтобы объекты двигались случайно, обновляйте их позиции в методе update, добавляя случайные приращения dx и dy.
def update(self, delta_time):
for i in range(NUM_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)Примечание: при таком подходе объекты могут постепенно «убегать» за экран — добавьте ограничение по границам или телепорт при выходе.
Объекты движутся к игроку
Чтобы объекты целенаправленно приближались к игроку, вычислите вектор направления, нормализуйте его и умножьте на скорость. Не забудьте импортировать math.
import math
def update(self, delta_time):
for i in range(NUM_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)Совет: всегда проверяйте деление на ноль при нормализации векторов.
Активация объектов по близости к игроку
Чтобы объекты начинали двигаться только когда игрок попадает в их окружение, добавьте проверку радиуса активации.
def update(self, delta_time):
for i in range(NUM_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:
dx /= distance
dy /= distance
x += dx * 3
y += dy * 3
self.objects[i] = (x, y)Параметр радиуса (здесь 100) можно выносить в константу или в настройки уровня.
Детекция коллизий и взаимодействие
Обработка столкновений позволит реагировать на контакт игрока и объектов — например, удалять объект и заново генерировать его в случайном месте.
def update(self, delta_time):
for i in range(NUM_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 < 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 и ограничивайте скорость компоненты dx и dy.
# в начале модули/константы
MAX_SPEED = 5
# в update
speed = 3
vx = dx * speed
vy = dy * speed
vx = max(min(vx, MAX_SPEED), -MAX_SPEED)
vy = max(min(vy, MAX_SPEED), -MAX_SPEED)
x += vx
y += vyКонтроль частоты спавна
Чтобы ограничить количество объектов, храните время последнего спавна и сравнивайте с задержкой SPAWN_DELAY.
import time
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 = []
self.last_spawn_time = time.time()
def update(self, delta_time):
# control the spawning rate here
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()
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 < 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)Настройте SPAWN_DELAY и MAX_OBJECTS для желаемого уровня сложности.
Практические советы и улучшения
- Контролируйте попадание за границы экрана: отражайте скорость или телепортируйте
- Используйте arcade.Sprite и SpriteList для оптимизации при большом количестве объектов
- Для естественного движения применяйте шум Перлина или алгоритмы «стирания» случайных векторов
- Для более сложного поведения реализуйте состояния у объектов (спокойный, агрессивный, отступающий)
Альтернативные подходы
- Arcade SpriteList: храните объекты как Sprite с velocity и используйте встроенную коллизию.
- Steering behaviors: seek, flee, wander — паттерны поведения из игровой ИИ.
- Физические движки: pymunk для сложной физики столкновений и реалистичных отклонений.
Контрпримеры когда это не подходит
- Повествовательные игры, где предсказуемость и режиссура важнее хаоса
- Игры для малышей, где слишком динамичное окружение ухудшает восприятие
- Сетевые игры, если поведение объектов отдельно не синхронизировано между клиентами
Чек-лист ролей
- Дизайнер: выставить MAX_OBJECTS, SPAWN_DELAY, скорость и радиус активации
- Разработчик: реализовать безопасную итерацию по спискам и границы поля
- QA: проверить коллизии, утечки памяти, производительность при пиковых значениях
Тесты и критерии приёмки
Критерии приёмки:
- Объекты корректно генерируются в пределах экрана
- При столкновении объект исчезает и создаётся новый
- Объекты не вызывают исключений при выходе за границы
- Производительность устойчиво держится на целевом FPS при MAX_OBJECTS
Минимальные тесты:
- Проверка спавна: объекты создаются через SPAWN_DELAY и не превосходят MAX_OBJECTS
- Проверка коллизий: при пересечении радиусов выполняется логика столкновения
- Проверка границ: объекты не создаются за пределами диапазона координат
Быстрые шаблоны и сниппеты
Пример использования SpriteList для движения и коллизий:
# упрощённый пример
player = arcade.Sprite("player.png", scale=0.5)
enemies = arcade.SpriteList()
for _ in range(NUM_OBJECTS):
e = arcade.Sprite("enemy.png", scale=0.3)
e.center_x = random.randint(0, SCREEN_WIDTH)
e.center_y = random.randint(0, SCREEN_HEIGHT)
enemies.append(e)
# в update: enemies.update(); проверка столкновений через arcade.check_for_collision_with_list(player, enemies)Факты и числа
- Ширина окна: 800
- Высота окна: 600
- Радиус игрока: 15
- Радиус объекта: 10
- Радиус активации по умолчанию в примерах: 100
Безопасность и производительность
- При большом количестве объектов используйте SpriteList и batch-отрисовку
- Ограничивайте частоту спавна и общее число объектов
- Для серверной валидации поведения объектов синхронизируйте только необходимые параметры
Короткая версия для анонса
Добавьте динамики в вашу 2D-игру: в статье показано, как генерировать случайно движущиеся объекты в Python с Arcade, как заставить их приближаться к игроку, как обрабатывать коллизии и как сбалансировать сложность с помощью ограничения скорости и контроля спавна.
Резюме
- Начните с простого списка координат или используйте SpriteList
- Для приближения используйте нормализованный вектор и ограничьте скорость
- Контролируйте спавн и количество объектов для стабильной производительности
- Тестируйте коллизии, границы экрана и поведение при граничных значениях
Важно: при нормализации всегда проверяйте деление на ноль и аккуратно работайте с удалением элементов из коллекций.
Похожие материалы
Как скачать полноразмерный аватар Discord
CSV в Node.js: fs и fast-csv — чтение и запись
Как увеличить экран на ПК — все способы
Как найти человека в WhatsApp — быстро и просто
Подсказки формул в Excel — руководство