Code of the Day
IntermediateSprites and Levels

The Sprite class

pygame.sprite.Sprite enforces a contract — image plus rect — that lets Groups manage update, draw, and collision for every member at once.

Game DevIntermediate6 min read
By the end of this lesson you will be able to:
  • Explain pygame.sprite.Sprite (image, rect, update(), kill())
  • Understand sprite Groups — update-all, draw-all, and collision queries
  • Describe the sprite lifecycle from creation through kill()

In the beginner labs every moving object was a pygame.Rect and a matching entry in a list you iterated manually. That works for a handful of objects. When a game has players, multiple enemy types, bullets, coins, and explosions — each potentially numbering in the dozens — the manual bookkeeping becomes fragile. pygame.sprite.Sprite and pygame.sprite.Group automate it.

The contract: image and rect

pygame.sprite.Sprite is a base class that enforces two attributes:

self.image — a pygame.Surface that is drawn when the sprite is rendered. For a placeholder, draw onto a fresh Surface. For a real game, load a PNG with pygame.image.load().

self.rect — a pygame.Rect that describes where on the screen the sprite lives. Its position is what Group.draw() uses as the blit destination.

These two attributes are the entire contract. As long as a subclass assigns both, pygame's group machinery works without any additional configuration.

Groups

A pygame.sprite.Group is a container that provides three key methods:

group.update(*args) calls update(*args) on every sprite in the group. You pass whatever arguments your sprites expect — typically the event list, the delta time, or both.

group.draw(surface) blits every sprite's image at its rect position onto surface. What would have been a manual loop in the beginner labs is now one line.

pygame.sprite.groupcollide(group_a, group_b, kill_a, kill_b) returns a dict of all collisions between the two groups. The boolean flags say whether to kill() colliding sprites. This replaces the nested loops you would otherwise write for collision detection.

The lifecycle

Sprites go through a predictable lifecycle:

  1. Create — instantiate the subclass, set image and rect.
  2. Add to group(s) — one sprite can belong to multiple groups simultaneously. You might have an all_sprites group for update/draw and a separate enemies group for collision queries.
  3. Update and draw — each frame, call group.update() then group.draw().
  4. Kill — when a sprite should be removed, call sprite.kill(). It removes itself from every group it belongs to. The Python garbage collector reclaims the object once no other references remain.

Note that kill() does not call any cleanup logic by default. If a sprite holds external resources (an open file, a subscribed event bus handler), you should override kill() or provide a separate cleanup method.

pygame.sprite.GroupSingle is a variant that holds exactly one sprite and is useful for the player. It exposes the same interface as Group so you can swap between them without changing calling code.

What this buys you

With Sprites and Groups:

  • Adding a new enemy is Enemy(all_sprites, enemies) — it registers itself into both groups in its __init__.
  • Updating all enemies is enemies.update() — one call regardless of count.
  • Detecting bullet-enemy collisions is pygame.sprite.groupcollide(bullets, enemies, True, True) — kill both on contact.
  • The update loop does not grow as the game adds new object types.

Where to go next

Next: sprite groups in practice — subclass Sprite for both player and enemy, put them in groups, and use groupcollide() for collision detection.

Finished reading? Mark it complete to track your progress.

On this page