pygame · v2.0.0 · 2026-09-23 · sha256 065e31a3e8ac2b9e

pygame v2.0.0A

Immutable. This exact content never changes and is served at /api/v1/blob/065e31a3e8ac2b9e.

---
name: pygame
description: Use when building 2D games in Python with pygame - game loop and delta time, sprites and groups, collision detection, drawing and surfaces, events and input, sound and fonts, camera, or performance optimization
metadata:
  author: mte90
  version: 2.0.0
  tags:
    - python
    - game-development
    - 2d-games
    - pygame
    - graphics
    - game-engine
---

# Pygame

Python game development library.

## Overview

Pygame is a set of Python modules designed for writing video games. It provides functionality for creating graphics, handling input, playing sounds, and more.

**Key Features:**
- 2D graphics and sprites
- Event handling
- Sound and music playback
- Font rendering
- Collision detection
- Game loops
- Hardware acceleration

### Installation

```bash
# Install pygame-ce (Community Edition) - recommended
pip install pygame-ce

# Legacy pygame (no longer maintained)
pip install pygame

# With additional features
pip install pygame-ce[fonts]
```

### pygame-ce (Community Edition)

pygame-ce is the maintained fork of pygame. Key differences:

- 20-30% faster performance in many benchmarks
- `IS_CE` flag to detect pygame-ce
- Better Python 3.10+ support
- Active development and bug fixes

```python
import pygame
print(pygame.ver)  # '2.x.x' for pygame-ce, '1.x.x' for legacy

# Check if using pygame-ce
if hasattr(pygame, 'IS_CE'):
    print("Using pygame-ce!")
```

## Getting Started

### Basic Window

```python
import pygame
import sys

# Initialize pygame
pygame.init()

# Create window
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("My Game")

# Game loop
running = True
while running:
    # Event handling
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                running = False
    
    # Drawing
    screen.fill((0, 0, 0))  # Black background
    pygame.draw.rect(screen, (255, 0, 0), (100, 100, 50, 50))
    
    # Update display
    pygame.display.flip()

# Quit pygame
pygame.quit()
sys.exit()
```

## Events

### Event Types

```python
for event in pygame.event.get():
    # Quit
    if event.type == pygame.QUIT:
        running = False
    
    # Key pressed
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_SPACE:
            print("Space pressed")
        if event.key == pygame.K_ESCAPE:
            running = False
    
    # Key released
    if event.type == pygame.KEYUP:
        if event.key == pygame.K_SPACE:
            print("Space released")
    
    # Mouse clicked
    if event.type == pygame.MOUSEBUTTONDOWN:
        x, y = event.pos
        button = event.button
        if button == 1:  # Left click
            print(f"Left click at {x}, {y}")
    
    # Mouse motion
    if event.type == pygame.MOUSEMOTION:
        x, y = event.pos
        rel_x, rel_y = event.rel
    
    # Joystick events
    if event.type == pygame.JOYBUTTONDOWN:
        if event.button == 0:  # A button
            print("Joystick A pressed")
    
    # Window events
    if event.type == pygame.WINDOWFOCUSLOST:
        print("Window lost focus")
    if event.type == pygame.WINDOWRESIZED:
        print(f"Window resized to {event.x}x{event.y}")
```

### Input States

```python
# Keyboard state
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
    print("Space is held down")

# Mouse state
mouse_pos = pygame.mouse.get_pos()
mouse_buttons = pygame.mouse.get_pressed()
if mouse_buttons[0]:  # Left button
    print("Left mouse button held")

# Joystick state
joystick = pygame.joystick.Joystick(0)
axes = joystick.get_numaxes()
for i in range(axes):
    value = joystick.get_axis(i)
```

## Collision Detection

```python
# Rectangle collision
if pygame.sprite.collide_rect(sprite1, sprite2):
    print("Collision!")

# Group collision
hits = pygame.sprite.spritecollide(player, enemies, True)  # Kill enemies
for enemy in hits:
    score += 10

# Group vs group
pygame.sprite.groupcollide(bullets, enemies, True, True)  # Kill both

# Circle collision (requires radius attribute)
pygame.sprite.collide_circle(sprite1, sprite2)

# Group circle collision
pygame.sprite.spritecollide(player, enemies, True, pygame.sprite.collide_circle)

# Mask collision (pixel-perfect)
mask1 = pygame.mask.from_surface(sprite1.image)
mask2 = pygame.mask.from_surface(sprite2.image)
if sprite1.rect.colliderect(sprite2.rect):  # First check bounding
    offset = (sprite2.rect.x - sprite1.rect.x, sprite2.rect.y - sprite1.rect.y)
    if mask1.overlap(mask2, offset):
        print("Pixel-perfect collision!")
```

## Sound

```python
# Initialize mixer
pygame.mixer.init()

# Load sound
shoot_sound = pygame.mixer.Sound("shoot.wav")
explosion_sound = pygame.mixer.Sound("explosion.wav")

# Set volume
shoot_sound.set_volume(0.5)
explosion_sound.set_volume(0.8)

# Play sound
shoot_sound.play()
shoot_sound.play(maxtime=500)  # Stop after 500ms

# Load music (streaming)
pygame.mixer.music.load("bgm.mp3")
pygame.mixer.music.play(-1)  # Loop forever
pygame.mixer.music.pause()
pygame.mixer.music.unpause()
pygame.mixer.music.stop()

# Music volume
pygame.mixer.music.set_volume(0.5)
```

## Fonts

```python
# Initialize font system
pygame.font.init()

# Get default font
font = pygame.font.Font(None, 36)  # Default system font, size 36

# Load custom font
font = pygame.font.Font("custom.ttf", 36)

# Render text
text_surface = font.render("Hello, World!", True, WHITE)  # Antialiased
text_surface = font.render("Hello", False, RED)  # Not antialiased

# Get text size
width, height = font.size("Hello")

# Draw text on screen
screen.blit(text_surface, (x, y))
```

## Time and Delta Time

```python
import pygame
import time

# Clock
clock = pygame.time.Clock()

# Set framerate
clock.tick(60)  # 60 FPS
fps = clock.get_fps()

# Delta time (for consistent movement)
last_time = time.time()
while True:
    dt = time.time() - last_time
    last_time = time.time()
    
    # Move at consistent speed regardless of framerate
    player.x += player.speed * dt
```

### Fixed Timestep Game Loop

The basic `clock.tick(fps)` approach varies the timestep when framerate drops, causing inconsistent physics. Fixed timestep updates physics at regular intervals while allowing interpolated rendering:

```python
class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((800, 600))
        self.clock = pygame.time.Clock()
        self.running = True
        
        # Fixed timestep configuration
        self.fixed_dt = 1/60  # 60 Hz physics
        self.accumulator = 0.0
        
    def run(self):
        while self.running:
            # Calculate delta time (in seconds)
            dt = self.clock.tick(60) / 1000.0
            
            # Handle events
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False
            
            # Fixed timestep update loop
            self.accumulator += dt
            while self.accumulator >= self.fixed_dt:
                self.fixed_update(self.fixed_dt)
                self.accumulator -= self.fixed_dt
            
            # Render with interpolation (smooths visual updates)
            interpolation = self.accumulator / self.fixed_dt
            self.render(interpolation)
        
        pygame.quit()
    
    def fixed_update(self, dt):
        """Physics/update at fixed 60 Hz"""
        # All game logic here - movement, collision, AI
        player.update_physics(dt)
    
    def render(self, interp):
        """Render with interpolation factor (0.0 to 1.0)"""
        self.screen.fill((0, 0, 0))
        
        # Interpolate positions for smooth rendering
        for sprite in all_sprites:
            render_x = sprite.x + (sprite.vx * interp)
            render_y = sprite.y + (sprite.vy * interp)
            self.screen.blit(sprite.image, (render_x, render_y))
        
        pygame.display.flip()
```

**Why fixed timestep matters:**
- Consistent physics regardless of framerate
- Deterministic network games (same simulation everywhere)
- No "spiral of death" when frame time exceeds update time
- Interpolation makes rendering smooth even when physics runs at lower rate

## Deep Dives

For detailed implementations, load these reference files on demand:

- **Drawing & Surfaces**: `references/drawing-surfaces.md` — Colors, shapes, surface operations, transforms
- **Performance Optimization**: `references/performance.md` — Image conversion, RLEACCEL, batched blits, dirty rects, pre-rendered surfaces
- **Sprites, Camera & UI**: `references/sprites-camera-ui.md` — Sprite classes, groups, camera implementation, button UI elements

## Best Practices

### 1. Use Sprite Groups

```python
# Good: Use groups for efficient updates
all_sprites = pygame.sprite.Group()
all_sprites.update()  # Updates all sprites
all_sprites.draw(screen)  # Draws all sprites
```

### 2. Delta Time

```python
# Good: Frame-rate independent movement
player.x += speed * dt
```

### 3. Pre-load Resources

```python
# Good: Load images/sounds once at startup
def load_game():
    global player_image, enemy_image, shoot_sound
    player_image = pygame.image.load("player.png").convert_alpha()
    enemy_image = pygame.image.load("enemy.png").convert_alpha()
    shoot_sound = pygame.mixer.Sound("shoot.wav")
```

### 4. Clean Exit

```python
# Good: Proper cleanup
try:
    game_loop()
finally:
    pygame.quit()
    sys.exit()
```

## Game Loop Template

```python
import pygame
import sys

class Game:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode((800, 600))
        self.clock = pygame.time.Clock()
        self.running = True
    
    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.running = False
    
    def update(self):
        pass
    
    def draw(self):
        self.screen.fill((0, 0, 0))
        pygame.display.flip()
    
    def run(self):
        while self.running:
            dt = self.clock.tick(60) / 1000.0  # Delta time in seconds
            self.handle_events()
            self.update()
            self.draw()
        pygame.quit()
        sys.exit()

if __name__ == "__main__":
    game = Game()
    game.run()
```

## References

- **Official Documentation**: https://www.pygame.org/docs/
- **Pygame Wiki**: https://www.pygame.org/wiki/
- **KidsCanCode Pygame Tutorials**: https://kidscancode.org/pygame_tutorials/