Editor

Sprites and groups

Pygame fundamentals · Lesson 7 of 7

In a bigger game, a class for each kind of thing keeps the code tidy, and sprite groups update, draw and collide them all at once.

A class for each kind of thing

A sprite is an object with an image (a Surface to draw) and a rect (where to draw it). Make a class that inherits from pygame.sprite.Sprite, call super().__init__(), set those two, and put its behaviour in update().

Here the player, the bullets and the enemies each get a class, and each class knows how it moves.

Groups do the looping for you

A pygame.sprite.Group holds sprites. group.update() calls update() on each of them, and group.draw(screen) draws each image at its rect, so the main loop stays short however many things there are.

sprite.kill() removes a sprite from every group it is in: the bullet that leaves the screen deletes itself.

Collisions between groups

pygame.sprite.groupcollide(bullets, enemies, True, True) finds every bullet touching an enemy and, with the two Trues, removes both. It returns what it found, so its length is the number of hits. pygame.sprite.spritecollideany(player, enemies) asks whether the player touches any enemy.

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((480, 320))
clock = pygame.time.Clock()
font = pygame.font.Font(None, 30)


class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((34, 20), pygame.SRCALPHA)
        pygame.draw.polygon(self.image, (255, 212, 59), [(17, 0), (0, 20), (34, 20)])
        self.rect = self.image.get_rect(midbottom=(240, 310))

    def update(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.rect.x -= 5
        if keys[pygame.K_RIGHT]:
            self.rect.x += 5
        self.rect.clamp_ip(screen.get_rect())


class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((4, 10))
        self.image.fill((255, 212, 59))
        self.rect = self.image.get_rect(midbottom=(x, y))

    def update(self):
        self.rect.y -= 8
        if self.rect.bottom < 0:
            self.kill()  # gone from every group


class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((26, 20), pygame.SRCALPHA)
        pygame.draw.rect(self.image, (90, 162, 224), (0, 0, 26, 20), border_radius=6)
        self.rect = self.image.get_rect(topleft=(random.randint(0, 454), -20))
        self.speed = random.uniform(1, 2.5)
        self.y = float(self.rect.y)

    def update(self):
        self.y += self.speed
        self.rect.y = round(self.y)
        if self.rect.top > 320:
            self.kill()


player = Player()
everything = pygame.sprite.Group(player)
bullets = pygame.sprite.Group()
enemies = pygame.sprite.Group()
score = 0
timer = 0

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
            bullet = Bullet(player.rect.centerx, player.rect.top)
            bullets.add(bullet)
            everything.add(bullet)

    timer += 1
    if timer % 40 == 0:
        enemy = Enemy()
        enemies.add(enemy)
        everything.add(enemy)

    everything.update()
    hits = pygame.sprite.groupcollide(bullets, enemies, True, True)
    score += len(hits) * 10
    if pygame.sprite.spritecollideany(player, enemies):
        for enemy in enemies:
            enemy.kill()  # a hit clears the sky and costs 50 points
        score = max(0, score - 50)

    screen.fill((16, 18, 22))
    everything.draw(screen)
    screen.blit(font.render(f"Score {score}", True, (230, 232, 235)), (12, 10))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Your turn

Change the programs above in the game panel, then press Run.

  • Give enemies a second kind: a red one that is worth 30 points and moves faster.
  • Add lives: three hits and the game is over.
  • Make enemies drift towards the player a little as they fall.

Try rewriting Space Shooter with sprite classes: it is the same game in fewer lines of loop. Open Space Shooter.