Cross-platform library for developing multimedia applications and video games in Python built on top of SDL
Sprite and collision system for managing game objects. Pygame's sprite system provides an organized way to handle game entities with automatic updating, rendering, and collision detection capabilities.
Foundation class for all game objects with lifecycle management.
class Sprite:
def __init__(self, *groups):
"""
Base class for visible game objects.
Args:
*groups: Sprite groups to add this sprite to
"""
def update(self, *args, **kwargs) -> None:
"""
Update sprite state (override in subclasses).
Args:
*args, **kwargs: Arguments passed from group.update()
"""
def kill(self) -> None:
"""Remove sprite from all groups."""
def alive(self) -> bool:
"""
Check if sprite is in any groups.
Returns:
bool: True if sprite belongs to at least one group
"""
def groups(self) -> list:
"""
Get list of groups containing this sprite.
Returns:
list: List of groups this sprite belongs to
"""
def add(self, *groups) -> None:
"""
Add sprite to groups.
Args:
*groups: Groups to add sprite to
"""
def remove(self, *groups) -> None:
"""
Remove sprite from groups.
Args:
*groups: Groups to remove sprite from
"""
# Required attributes for proper sprite behavior:
image: pygame.Surface # Visual representation
rect: pygame.Rect # Position and collision boundsContainer classes for managing collections of sprites.
class Group:
def __init__(self, *sprites):
"""
Container for sprites with batch operations.
Args:
*sprites: Initial sprites to add
"""
def sprites(self) -> list:
"""
Get list of all sprites in group.
Returns:
list: List of sprites in group
"""
def copy(self) -> 'Group':
"""
Create a copy of the group.
Returns:
Group: New group with same sprites
"""
def add(self, *sprites) -> None:
"""
Add sprites to group.
Args:
*sprites: Sprites to add
"""
def remove(self, *sprites) -> None:
"""
Remove sprites from group.
Args:
*sprites: Sprites to remove
"""
def has(self, *sprites) -> bool:
"""
Check if group contains sprites.
Args:
*sprites: Sprites to check
Returns:
bool: True if all sprites are in group
"""
def update(self, *args, **kwargs) -> None:
"""
Call update() on all sprites in group.
Args:
*args, **kwargs: Arguments passed to sprite.update()
"""
def draw(self, surface: pygame.Surface) -> list:
"""
Draw all sprites to a surface.
Args:
surface (pygame.Surface): Surface to draw on
Returns:
list: List of Rect areas that were drawn
"""
def clear(self, surface: pygame.Surface, background) -> list:
"""
Clear sprites by drawing background over them.
Args:
surface (pygame.Surface): Surface to clear on
background: Background to draw (Surface or color)
Returns:
list: List of Rect areas that were cleared
"""
def empty(self) -> None:
"""Remove all sprites from group."""
def __len__(self) -> int:
"""Get number of sprites in group."""
def __iter__(self):
"""Iterate over sprites in group."""
def __contains__(self, sprite) -> bool:
"""Check if sprite is in group."""Advanced group classes with additional features for game development.
class RenderUpdates(Group):
def __init__(self, *sprites):
"""Group that tracks dirty rectangles for efficient screen updates."""
def draw(self, surface: pygame.Surface) -> list:
"""
Draw sprites and return list of updated areas.
Returns:
list[pygame.Rect]: Areas that were drawn (for display updates)
"""
class OrderedUpdates(RenderUpdates):
def __init__(self, *sprites):
"""Group that maintains sprite order for drawing."""
class LayeredUpdates(OrderedUpdates):
def __init__(self, *sprites, **kwargs):
"""
Group with sprite layers for depth sorting.
Keyword Args:
default_layer (int): Default layer for new sprites
"""
def add(self, *sprites, **kwargs) -> None:
"""
Add sprites to group with optional layer.
Keyword Args:
layer (int): Layer to add sprites to
"""
def change_layer(self, sprite, new_layer: int) -> None:
"""
Move sprite to different layer.
Args:
sprite: Sprite to move
new_layer (int): New layer number
"""
def get_layer_of_sprite(self, sprite) -> int:
"""
Get layer of a sprite.
Args:
sprite: Sprite to check
Returns:
int: Layer number
"""
def get_sprites_at(self, layer: int) -> list:
"""
Get all sprites at a specific layer.
Args:
layer (int): Layer to check
Returns:
list: Sprites at the layer
"""
def layers(self) -> list:
"""
Get list of all layers.
Returns:
list[int]: Sorted list of layer numbers
"""
class GroupSingle(Group):
def __init__(self, sprite = None):
"""
Group that holds only one sprite at a time.
Args:
sprite: Initial sprite (optional)
"""
@property
def sprite(self):
"""Get the single sprite in group (or None)."""Specialized sprite classes with additional functionality.
class DirtySprite(Sprite):
def __init__(self, *groups):
"""Sprite that tracks changes for efficient rendering."""
dirty: int # 0=clean, 1=dirty, 2=always dirty
source_rect: pygame.Rect # Source area for blitting
visible: int # Visibility flag
class WeakSprite(Sprite):
def __init__(self, *groups):
"""Sprite with weak references to groups (for memory management)."""
class WeakDirtySprite(WeakSprite, DirtySprite):
def __init__(self, *groups):
"""Sprite combining weak references and dirty tracking."""Functions for detecting collisions between sprites and groups.
def spritecollide(sprite, group: Group, dokill: bool, collided = None) -> list:
"""
Find sprites in group that collide with given sprite.
Args:
sprite: Sprite to check collisions for
group (Group): Group to check against
dokill (bool): Remove colliding sprites from group
collided: Collision detection function (default: collide_rect)
Returns:
list: List of sprites that collide
"""
def groupcollide(group1: Group, group2: Group, dokill1: bool, dokill2: bool, collided = None) -> dict:
"""
Detect collisions between two sprite groups.
Args:
group1 (Group): First group
group2 (Group): Second group
dokill1 (bool): Remove colliding sprites from group1
dokill2 (bool): Remove colliding sprites from group2
collided: Collision detection function
Returns:
dict: Mapping of group1 sprites to list of colliding group2 sprites
"""
def spritecollideany(sprite, group: Group, collided = None):
"""
Find first sprite in group that collides with given sprite.
Args:
sprite: Sprite to check collisions for
group (Group): Group to check against
collided: Collision detection function
Returns:
Sprite or None: First colliding sprite found
"""Different collision detection algorithms for various game needs.
def collide_rect(left, right) -> bool:
"""
Collision detection using sprite rectangles.
Args:
left, right: Sprites to check
Returns:
bool: True if rectangles overlap
"""
def collide_rect_ratio(ratio: float):
"""
Create collision function using scaled rectangles.
Args:
ratio (float): Scale factor for rectangles
Returns:
function: Collision detection function
"""
def collide_circle(left, right) -> bool:
"""
Collision detection using circular areas.
Args:
left, right: Sprites to check (must have .radius attribute)
Returns:
bool: True if circles overlap
"""
def collide_circle_ratio(ratio: float):
"""
Create collision function using scaled circles.
Args:
ratio (float): Scale factor for circles
Returns:
function: Collision detection function
"""
def collide_mask(left, right):
"""
Pixel-perfect collision detection using masks.
Args:
left, right: Sprites to check (must have .mask attribute)
Returns:
tuple or None: Collision point or None if no collision
"""import pygame
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
# Required sprite attributes
self.image = pygame.Surface((32, 32))
self.image.fill((0, 128, 255)) # Blue player
self.rect = self.image.get_rect()
self.rect.center = (400, 300)
# Custom attributes
self.speed = 5
def update(self):
# Move based on keyboard input
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.rect.x -= self.speed
if keys[pygame.K_RIGHT]:
self.rect.x += self.speed
if keys[pygame.K_UP]:
self.rect.y -= self.speed
if keys[pygame.K_DOWN]:
self.rect.y += self.speed
# Keep player on screen
self.rect.clamp_ip(pygame.Rect(0, 0, 800, 600))
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((24, 24))
self.image.fill((255, 0, 0)) # Red enemy
self.rect = self.image.get_rect()
self.rect.center = (x, y)
self.speed = 2
self.direction = 1
def update(self):
# Simple back and forth movement
self.rect.x += self.speed * self.direction
if self.rect.left < 0 or self.rect.right > 800:
self.direction *= -1
# Initialize pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# Create sprite groups
all_sprites = pygame.sprite.Group()
enemies = pygame.sprite.Group()
# Create player
player = Player()
all_sprites.add(player)
# Create enemies
for i in range(5):
enemy = Enemy(100 + i * 120, 100)
all_sprites.add(enemy)
enemies.add(enemy)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Update all sprites
all_sprites.update()
# Check collisions
hits = pygame.sprite.spritecollide(player, enemies, True)
for hit in hits:
print("Player hit enemy!")
# Draw everything
screen.fill((0, 0, 0))
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()import pygame
import math
class Ball(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
# Create circular sprite
self.radius = 15
self.image = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
pygame.draw.circle(self.image, (255, 255, 0), (self.radius, self.radius), self.radius)
self.rect = self.image.get_rect()
self.rect.center = (x, y)
# Physics
self.vel_x = random.uniform(-5, 5)
self.vel_y = random.uniform(-5, 5)
def update(self):
self.rect.x += self.vel_x
self.rect.y += self.vel_y
# Bounce off walls
if self.rect.left < 0 or self.rect.right > 800:
self.vel_x *= -1
if self.rect.top < 0 or self.rect.bottom > 600:
self.vel_y *= -1
import random
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# Create balls
balls = pygame.sprite.Group()
for _ in range(10):
ball = Ball(random.randint(50, 750), random.randint(50, 550))
balls.add(ball)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
balls.update()
# Check ball-to-ball collisions using circle collision
for ball in balls:
collisions = pygame.sprite.spritecollide(ball, balls, False, pygame.sprite.collide_circle)
for other in collisions:
if ball != other:
# Simple collision response
dx = ball.rect.centerx - other.rect.centerx
dy = ball.rect.centery - other.rect.centery
distance = math.sqrt(dx*dx + dy*dy)
if distance > 0:
# Normalize and separate
dx /= distance
dy /= distance
ball.rect.centerx += dx * 2
ball.rect.centery += dy * 2
screen.fill((0, 0, 0))
balls.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()import pygame
class Background(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((800, 600))
self.image.fill((50, 100, 50)) # Green background
self.rect = self.image.get_rect()
class Player(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill((0, 0, 255)) # Blue player
self.rect = self.image.get_rect()
self.rect.center = (400, 300)
class Bullet(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((4, 8))
self.image.fill((255, 255, 0)) # Yellow bullet
self.rect = self.image.get_rect()
self.rect.centerx = x
self.rect.bottom = y
self.speed = -10
def update(self):
self.rect.y += self.speed
if self.rect.bottom < 0:
self.kill()
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# Use layered group for depth sorting
all_sprites = pygame.sprite.LayeredUpdates()
# Add sprites to specific layers (lower numbers = behind, higher = in front)
background = Background()
all_sprites.add(background, layer=0)
player = Player()
all_sprites.add(player, layer=2)
bullets = pygame.sprite.Group()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
bullet = Bullet(player.rect.centerx, player.rect.top)
all_sprites.add(bullet, layer=1) # Behind player
bullets.add(bullet)
all_sprites.update()
screen.fill((0, 0, 0))
all_sprites.draw(screen) # Draws in layer order
pygame.display.flip()
clock.tick(60)
pygame.quit()import pygame
class MovingSprite(pygame.sprite.DirtySprite):
def __init__(self, x, y, color):
super().__init__()
self.image = pygame.Surface((20, 20))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.center = (x, y)
# DirtySprite attributes
self.dirty = 1 # Start dirty
self.vel_x = random.uniform(-3, 3)
self.vel_y = random.uniform(-3, 3)
def update(self):
old_rect = self.rect.copy()
self.rect.x += self.vel_x
self.rect.y += self.vel_y
# Bounce off walls
if self.rect.left < 0 or self.rect.right > 800:
self.vel_x *= -1
if self.rect.top < 0 or self.rect.bottom > 600:
self.vel_y *= -1
# Mark as dirty if moved
if self.rect != old_rect:
self.dirty = 1
import random
pygame.init()
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
# Use RenderUpdates for efficient screen updates
sprites = pygame.sprite.RenderUpdates()
# Create many moving sprites
for _ in range(50):
color = (random.randint(100, 255), random.randint(100, 255), random.randint(100, 255))
sprite = MovingSprite(random.randint(0, 800), random.randint(0, 600), color)
sprites.add(sprite)
background = pygame.Surface(screen.get_size())
background.fill((30, 30, 30))
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
sprites.update()
# Only update changed areas
dirty_rects = sprites.draw(screen)
pygame.display.update(dirty_rects)
# Clear sprites for next frame (only dirty areas)
sprites.clear(screen, background)
clock.tick(60)
pygame.quit()