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.
- 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:
- Create — instantiate the subclass, set
imageandrect. - Add to group(s) — one sprite can belong to multiple groups simultaneously.
You might have an
all_spritesgroup for update/draw and a separateenemiesgroup for collision queries. - Update and draw — each frame, call
group.update()thengroup.draw(). - 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.
Lab: State machine refactor
Refactor the polished beginner game into a three-scene state machine with an event bus for score updates and a camera offset for a scrolling level.
Sprite groups in practice
Subclass pygame.sprite.Sprite for Player and Enemy, use Groups for update and draw, and detect collisions with groupcollide().