Спрайты и группы
Когда объектов в игре десятки, держать их по одному — каша. Спрайт-классы и группы наводят порядок: каждый объект знает, как себя обновлять и рисовать.
Суть: pygame.sprite.Sprite — это объект с image и rect. Группа Sprite Group хранит много спрайтов и одной командой обновляет и рисует их всех.
Пока у тебя один герой — можно хранить его в паре переменных. Но как только появляются десять врагов, двадцать пуль и горсть монеток, код превращается в свалку из списков и циклов. Pygame предлагает аккуратное решение, пришедшее из объектно-ориентированного программирования: спрайты и группы.
Спрайт (Sprite) — это класс-наследник pygame.sprite.Sprite, у которого есть два обязательных поля: image (как он выглядит) и rect (где он находится). У спрайта есть метод update, в котором он сам решает, как себя двигать. Это инкапсуляция: каждый объект отвечает за себя.
Группа (Group) — это умный контейнер для спрайтов. Вместо ручного цикла по списку ты пишешь enemies.update(dt) — и группа вызовет update у каждого спрайта. А enemies.draw(screen) нарисует их всех. Добавить врага — enemies.add(enemy), удалить (например, убитого) — enemy.kill(), и он сам исчезнет из всех групп.
Как работает под капотом
Группа хранит ссылки на спрайты. Когда ты зовёшь group.update(), она в цикле дёргает sprite.update() у каждого. Когда зовёшь group.draw(screen) — делает blit(sprite.image, sprite.rect) для всех. Вот картина:
enemies = Group
|
+---------+---------+---------+
v v v v
enemy1 enemy2 enemy3 enemy4
image image image image
rect rect rect rect
enemies.update(dt) -> зовёт update у всех
enemies.draw(screen) -> blit всех на экран
enemy2.kill() -> выпадает из группы сам
Класс спрайта на pygame (читаем):
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.image.load("enemy.png").convert_alpha()
self.rect = self.image.get_rect(center=(x, y))
self.speed = 120
def update(self, dt):
self.rect.x += self.speed * dt # ползёт вправо
if self.rect.left > 800:
self.kill() # ушёл за экран — удаляем
enemies = pygame.sprite.Group()
enemies.add(Enemy(100, 200))
# в цикле:
enemies.update(dt)
enemies.draw(screen)Идею «группа обновляет всех» можно показать на чистом Python. Сделаем мини-группу из объектов, у каждого свой update. Попробуй сам:
class Enemy:
def __init__(self, name, x, speed):
self.name, self.x, self.speed = name, x, speed
def update(self, dt):
self.x += self.speed * dt
group = [Enemy("A", 0, 100), Enemy("B", 50, 200), Enemy("C", 90, 60)]
dt = 0.5 # полсекунды
for e in group: # это и делает group.update() внутри
e.update(dt)
for e in group:
print(f"{e.name}: x = {e.x:.0f}")Несколько групп для одного спрайта
Один и тот же спрайт может состоять сразу в нескольких группах, и это очень удобно. Типичный приём: завести общую группу all_sprites для отрисовки и отдельные специализированные группы (enemies, coins, bullets) для коллизий. Каждого врага добавляют и в all_sprites, и в enemies. Тогда all_sprites.draw(screen) рисует вообще всё одной строкой, а spritecollide(player, enemies, ...) проверяет столкновения только с врагами. Когда враг гибнет, kill() вынимает его из обеих групп разом.
Есть и специальные виды групп. GroupSingle хранит ровно один спрайт — идеально для игрока. LayeredUpdates рисует спрайты по слоям, уважая «глубину», чтобы герой проходил перед одними объектами и за другими. Начинать стоит с обычной Group, но знать о существовании специализированных контейнеров полезно: когда игра вырастет, они избавят от ручной возни. Группы — это не просто списки, а продуманный инструмент управления толпой объектов, и чем раньше ты начнёшь им доверять, тем чище будет твой код.
Частые ошибки
- Забыть
super().__init__()в спрайте — группа не сможет с ним работать. - Не задать
self.imageилиself.rect—group.drawупадёт. - Удалять спрайт из списка во время итерации — лучше
kill(), группа разрулит сама.
Best practices
- Каждый тип объекта — отдельный класс-спрайт со своим
update. - Держи спрайты в группах по смыслу:
enemies,bullets,coins. - Заведи общую группу
all_spritesдля отрисовки, а специальные — для коллизий.
Итог: спрайт знает, как себя обновлять и рисовать, а группа управляет толпой одной командой. Это масштабируемый порядок вместо хаоса.