Module 18 of 20 Phase: Architecture & Shipping

Game Architecture
Scenes, Config & Clean Structure

A 200-line prototype and a 5,000-line real game need very different code organization. This module introduces scene/state management, centralized asset loading, config constants, logging and a project layout that scales.

🏗️
12 Lessons
This module
~3 hrs
Estimated time
📊
Advanced
Difficulty
🐍
Scene pattern
Core technique
Section 1

Game States & Scene Management

A "scene" (or "state") is a self-contained screen — menu, gameplay, pause, game over — each handling its own events, updates and drawing. A SceneManager holds the current scene and swaps it out on request, so main.py never needs a giant if/elif chain.

scene.py
class Scene:
    def __init__(self, manager):
        self.manager = manager

    def handle_events(self, events): pass
    def update(self, dt): pass
    def draw(self, screen): pass


class SceneManager:
    def __init__(self):
        self.current = None

    def go_to(self, scene):
        self.current = scene

    def handle_events(self, events):
        self.current.handle_events(events)

    def update(self, dt):
        self.current.update(dt)

    def draw(self, screen):
        self.current.draw(screen)
menu_scene.py
class MenuScene(Scene):
    def handle_events(self, events):
        for e in events:
            if e.type == pygame.KEYDOWN and e.key == pygame.K_RETURN:
                self.manager.go_to(PlayScene(self.manager))

    def draw(self, screen):
        screen.fill((10, 14, 30))
        # draw title + "Press Enter" prompt
🏠
Scene
MenuScene
🎮
Scene
PlayScene
💀
Scene
GameOverScene
Section 2

Centralized Asset Loading

Rather than scattering pygame.image.load() calls across scene files, one module loads everything once at startup and every scene reads from the same shared dictionary — the exact pattern introduced in Module 4, now formalized project-wide.

assets.py
class Assets:
    images = {}
    sounds = {}

    @classmethod
    def load_all(cls):
        cls.images["player"] = pygame.image.load("assets/player.png").convert_alpha()
        cls.images["enemy"]  = pygame.image.load("assets/enemy.png").convert_alpha()
        cls.sounds["jump"]   = pygame.mixer.Sound("assets/jump.wav")
        # ... every asset the game needs, loaded exactly once
Section 3

Config Files & Constants

Magic numbers scattered through gameplay code (800, 1600, 0.7) become unreadable fast. A single config.py module holding named constants makes tuning game feel a one-line change instead of a hunt through every file.

config.py
SCREEN_WIDTH = 960
SCREEN_HEIGHT = 540
FPS = 60

GRAVITY = 1600
JUMP_FORCE = -650
PLAYER_SPEED = 300

WHITE = (255, 255, 255)
BG_COLOR = (5, 9, 26)
Section 4

Logging, Debugging & Project Organization

📝
Logging
Use Python's built-in logging module instead of scattered print() calls — it gives timestamps, severity levels (DEBUG/INFO/WARNING/ERROR), and can be silenced in a release build without deleting code.
🐛
Debugging
Draw hitboxes and FPS counters conditionally behind a DEBUG = True flag in config.py, so debug visuals never ship in the final build.
📁
Folder Layout
main.py, config.py, assets.py, scenes/, entities/, assets/images|sounds/ — separate code from content, and separate each concern into its own module.
🧩
Single Responsibility
Each class should do one job — a Player handles player logic, a Camera handles offsets, a ParticleSystem handles particles. Resist the urge to grow one giant "Game" god-class.
Section 5

The Layered Architecture at a Glance

Stacking the pieces from this module top to bottom shows the dependency direction clearly: each layer only depends on the layers below it, never the other way around.

Game Loop SceneManager Scenes (Menu / Play / GameOver) Assets & Config depends on
Each layer depends only on the layer(s) beneath it — never upward

🧠 Quick Knowledge Check

1. What problem does a SceneManager solve?
2. Why centralize asset loading into one Assets class instead of loading per-scene?
3. What's the benefit of putting numbers like gravity and jump force into config.py?
4. Why prefer Python's logging module over scattered print() statements?
5. What does the "single responsibility" principle suggest for a Player class?
Finished Module 18?

Mark it complete to track your progress through Complete Pygame Mastery 2026.

🗒 Cheat Sheet 📝 Worksheet