Skip to content

A clean, minimal ECS-driven Pygame demo showcasing responsive UI, scene management, animations, and event-based architecture.

License

Notifications You must be signed in to change notification settings

Johnny-Taake/ECSPygameDemo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

70 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

GuessNumberPygame

An example of building a game with ECS + Event Bus + Responsive UI in Pygame.

This project serves as an educational reference showing how to structure a small game professionally using Python and Pygame. It demonstrates clean architecture, responsive UI, scenes, ECS, animations, event-driven logic, and asset pipelines.


๐ŸŽฅ Demo

๐ŸŽฌ Gameplay demo:

Gameplay demo


๐Ÿ“ฆ Run executable (if available)

Open the dist/ folder and choose the appropriate executable for your system. Choose it if available, otherwise download the source code and follow the instructions down below. For Windows extract the .zip file and put the .exe anywhere you like. Then run it. For linux users - you know the drill. For macOS users - sorry, I don't have a Mac to test on. (# TODO: add macOS instructions).


โœจ What this project demonstrates

  • Lightweight ECS (Entityโ€“Componentโ€“System)
  • Responsive UI with virtual resolution & letterboxing
  • Scene management with transitions and lifecycle hooks
  • Event Bus for decoupled communication between systems
  • Asset Loader with progress display
  • Polished UI animations (button press, shadows, highlight effects)
  • Config system using pydantic / pydantic-settings
  • Logging, error handling, and a clean, scalable file structure

It is a blueprint for beginners and intermediate developers who want to learn solid architecture on a small, understandable game.


๐Ÿ“ Project Structure

GuessNumberPygame/
โ”œโ”€โ”€ main.py                      # Entry point: creates GameApp and starts the game loop
โ”‚
โ”œโ”€โ”€ build.py                     # Python helper script for building executables
โ”œโ”€โ”€ build.spec                   # PyInstaller spec for cross-platform builds
โ”‚
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ __init__.py              # Exports application module
โ”‚   โ””โ”€โ”€ application.py           # GameApp class: main loop, window, responsive scaling
โ”‚
โ”œโ”€โ”€ engine/                      # Core ECS / engine layer (framework, not game-specific)
โ”‚   โ”œโ”€โ”€ __init__.py              # Engine exports
โ”‚   โ”œโ”€โ”€ ecs.py                   # GameObject (entity) and component storage
โ”‚   โ”œโ”€โ”€ base_scene.py            # BaseScene class with lifecycle and fade handling
โ”‚   โ”œโ”€โ”€ scene_manager.py         # SceneManager: switching scenes safely
โ”‚   โ”œโ”€โ”€ service_locator.py       # ServiceLocator for global services (app, sound, etc.)
โ”‚   โ”œโ”€โ”€ event_bus.py             # EventBus: pub/sub for decoupled communication
โ”‚   โ”œโ”€โ”€ asset_loader.py          # AssetLoader: staged loading with progress
โ”‚   โ”œโ”€โ”€ ui_builder.py            # UIBuilder: factories for buttons, labels, image buttons
โ”‚   โ”œโ”€โ”€ components/              # ECS components
โ”‚   โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”‚   โ”œโ”€โ”€ alpha.py             # AlphaComponent: fade in/out & transparency animation
โ”‚   โ”‚   โ”œโ”€โ”€ button.py            # ButtonComponent: text, pressed state, hover, shortcuts
โ”‚   โ”‚   โ”œโ”€โ”€ headers.py           # H1/H2/H3 components for semantic text styling
โ”‚   โ”‚   โ”œโ”€โ”€ image.py             # ImageComponent: displays images (icons, sprites)
โ”‚   โ”‚   โ”œโ”€โ”€ input.py             # InputFieldComponent: text input fields
โ”‚   โ”‚   โ”œโ”€โ”€ label.py             # LabelComponent: simple text labels
โ”‚   โ”‚   โ”œโ”€โ”€ position.py          # Position: x/y coordinates in virtual space
โ”‚   โ”‚   โ”œโ”€โ”€ progress_bar.py      # ProgressBarComponent: loading bar for boot scene
โ”‚   โ”‚   โ””โ”€โ”€ sound.py             # SoundComponent: describes sounds to be played
โ”‚   โ””โ”€โ”€ systems/                 # Systems: logic that operates on components
โ”‚       โ”œโ”€โ”€ __init__.py
โ”‚       โ”œโ”€โ”€ render.py            # RenderSystem: draws UI, buttons, text, images, animations
โ”‚       โ”œโ”€โ”€ input.py             # InputSystem: mouse, keyboard, focus, button press logic
โ”‚       โ””โ”€โ”€ sound.py             # SoundSystem: loads & plays sound effects
โ”‚
โ”œโ”€โ”€ game/                        # Game-specific logic / scenes using the engine
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ logic.py                 # GameLogic: number generation, guess checking, statuses
โ”‚   โ””โ”€โ”€ scenes/
โ”‚       โ”œโ”€โ”€ __init__.py
โ”‚       โ”œโ”€โ”€ boot.py              # BootScene: staged asset loading with progress bar
โ”‚       โ”œโ”€โ”€ menu.py              # MenuScene: start game, toggle sound, etc.
โ”‚       โ”œโ”€โ”€ game.py              # GameScene: main gameplay (input, hints, attempts)
โ”‚       โ”œโ”€โ”€ win.py               # WinScene: win screen & attempts summary
โ”‚       โ”œโ”€โ”€ dialog.py            # DialogScene: modal overlays / messages
โ”‚       โ””โ”€โ”€ results_modal.py     # Results modal: highscore summary
โ”‚
โ”œโ”€โ”€ assets/                      # All runtime assets used by the game
โ”‚   โ”œโ”€โ”€ fonts/                   # Custom fonts
โ”‚   โ”‚   โ””โ”€โ”€ *.ttf
โ”‚   โ”œโ”€โ”€ images/                  # Icons & UI images
โ”‚   โ”‚   โ”œโ”€โ”€ mute.png
โ”‚   โ”‚   โ””โ”€โ”€ volume.png
โ”‚   โ”œโ”€โ”€ sounds/                  # Sound effects
โ”‚   โ”‚   โ”œโ”€โ”€ button-click.mp3
โ”‚   โ”‚   โ”œโ”€โ”€ keyboard-click.mp3
โ”‚   โ”‚   โ””โ”€โ”€ soft-treble-win-fade-out.mp3
โ”‚   โ””โ”€โ”€ icon.png                 # Window / application icon
โ”‚
โ”œโ”€โ”€ config/                      # Configuration system (pydantic-based)
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ base.py                  # Base models and shared config pieces
โ”‚   โ”œโ”€โ”€ game_config.py           # GameConfig: sizes, colors, FPS, UI constants
โ”‚   โ”œโ”€โ”€ logging.py               # Logging setup used by logger/
โ”‚   โ”œโ”€โ”€ settings.py              # Settings: env integration (.env, GAME_* vars)
โ”‚   โ””โ”€โ”€ ui.py                    # UI-specific config (padding, radii, colors)
โ”‚
โ”œโ”€โ”€ stats/                       # Simple game statistics / persistence layer
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ models.py                # Stats data models
โ”‚   โ”œโ”€โ”€ manager.py               # Stats management logic
โ”‚   โ””โ”€โ”€ storage.py               # Stats persistence layer
โ”‚
โ”œโ”€โ”€ utils/                       # Small helpers not tied to ECS
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ””โ”€โ”€ responsive.py            # ResponsiveScaleManager: virtual surface + scaling
โ”‚
โ”œโ”€โ”€ logger/                      # Logging convenience wrapper
โ”‚   โ”œโ”€โ”€ __init__.py              # get_logger(), setup_logging()
โ”‚   โ””โ”€โ”€ colored_formatter.py     # Colored log formatting
โ”‚
โ”œโ”€โ”€ tests/                       # Tests
โ”‚
โ”œโ”€โ”€ docs/                        # Additional documentation & guides
โ”‚   โ”œโ”€โ”€ UV.md                    # UV usage & command cheatsheet
โ”‚   โ”œโ”€โ”€ ADVANCED_ARCHITECTURE_GUIDE.md  # Deep-dive engine/architecture internals
โ”‚   โ”œโ”€โ”€ TESTS.md                 # Testing guide and test structure
โ”‚   โ””โ”€โ”€ demo.mp4                 # Gameplay demo video
โ”‚
โ”œโ”€โ”€ .env.example                 # Example environment configuration
โ”œโ”€โ”€ pyproject.toml               # Project dependencies and metadata (for uv/pip)
โ”œโ”€โ”€ uv.lock                      # Resolved dependency lockfile
โ””โ”€โ”€ README.md                    # **THIS FILE**

Architecture Overview

The project is intentionally structured like a mini real-world game codebase:

  • engine/ โ€“ reusable framework: ECS, scenes, event bus, asset loader, UI builder
  • game/ โ€“ game rules: number guessing, scenes for menu/game/win
  • app/ โ€“ application shell: Pygame window, virtual surface, main loop
  • config/ โ€“ configuration via pydantic, environment overrides
  • assets/ โ€“ all runtime assets in one place

For a deeper, maintainer-level description of internals (ECS details, scaling model, animation behavior, performance notes), see: ๐Ÿ“„ docs/ADVANCED_ARCHITECTURE_GUIDE.md Also see the testing guide for details on testing.


๐Ÿงฉ ECS (Entityโ€“Componentโ€“System)

Entities โ€“ lightweight containers (GameObject) Components โ€“ pure data (Position, Label, Button, Input, Image, Alpha, etc.) Systems โ€“ pure logic operating on entities that have the required components.

Example: creating a UI button entity via UIBuilder:

def button_entity(self, text: str, x: int, y: int, onclick, keyboard_shortcut: Optional[str] = None):
    e = GameObject()
    btn = ButtonComponent(text, keyboard_shortcut)
    btn.on_click = onclick
    e.add(Position(x, y)).add(btn)
    return e

Event Bus

The EventBus provides a simple pub/sub mechanism so systems and scenes can react to events without direct references to each other.

from engine.event_bus import event_bus

def on_play(name: str):
    sound_system.play(name)

event_bus.subscribe("sound:play", on_play)
event_bus.emit("sound:play", "click")

Used for:

  • UI button events
  • Scene transitions
  • Sound triggering
  • Input notifications

Responsive UI (Virtual Surface + Letterboxing)

The game renders into a fixed 640ร—400 virtual surface, then:

  1. Scales uniformly to fit the window
  2. Adds letterboxing (black borders) if the aspect ratio doesnโ€™t match
  3. Converts mouse coordinates from screen โ†’ virtual space, so the logic works in a consistent coordinate system

Implemented in utils/responsive.py.


๐ŸŽจ UI & Animation

Buttons and UI elements are designed to feel tactile:

  • Shadows and depth
  • Press animation (button โ€œsinksโ€ a few pixels on click)
  • Hover / active states
  • Gradient highlights

๐ŸŽถ Asset Loader

Assets are loaded gradually in the Boot Scene with progress feedback:

  • Fonts
  • Images
  • Sounds
  • Services

AssetLoader executes tasks over multiple frames to avoid freezing the UI:

def execute_next_task(self, dt: float):
    if self.current_task_index < len(self.tasks):
        self.current_task_frame_counter += 1
        if self.current_task_frame_counter >= self.frames_per_task:
            task = self.tasks[self.current_task_index]
            self.description = task.description
            task.execute()
            self.current_task_index += 1
            self.progress = self.current_task_index / len(self.tasks)
            self.current_task_frame_counter = 0
    else:
        self.completed = True

The boot scene displays a loading bar driven by progress.


๐ŸŽฎ Gameplay Summary

A simple but polished number-guessing game:

  • Guess a random number in a configurable range (e.g. 1โ€“N or -N to +N depending on difficulty choice)
  • Type your guess in an input field and submit
  • The game tells you if your guess is too low / too high
  • Counts attempts
  • Shows a win screen and basic stats
  • Maintains high scores / stats via the stats/ module
  • Navigation flow: Menu โ†’ Game โ†’ Win (with reset and back buttons)

Difficulty Levels

The game supports multiple difficulty levels with different number ranges:

  • Easy: 1-10
  • Medium: 1-100
  • Hard: 1-1000
  • Very Hard: 1-10000
  • Extreme: 1-100000
  • Impossible: -100000 to 100000 (supports negative numbers!)

Negative Number Support

The game fully supports negative numbers in difficulty ranges:

  • Games with negative ranges allow users to enter negative guesses (e.g., -50)
  • When typing negative numbers, there's no error shown for the minus sign (-) during input
  • If a game doesn't allow negative numbers, typing - will show "Number should be positive"
  • The UI validation is context-aware based on the current game's range

Controls:

  • Mouse for buttons and input focus
  • Keyboard for typing numbers
  • Optional hotkeys (e.g. ESC โ†’ back to menu)

๐Ÿ“ฆ Installation

Requirements

  • Python 3.13+
  • uv (optional, recommended)

.env file (optional)

Copy .env.example to .env and fill in the required values.

Install dependencies with uv

uv sync

To also install build / test / dev extras, see: ๐Ÿ“„ docs/UV.md

Or install via pip

pip install -r requirements.txt

Run the game

uv run main.py

or using venv:

python main.py

Building Executables

Via UV:

uv sync --extra build
uv run build.py

Via pip and venv:

pip install -r requirements.txt
python build.py

Or build with the spec file using PyInstaller:

python -m PyInstaller build.spec

The executable bundle will be generated into the dist/ directory.

More details: ๐Ÿ“„ docs/UV.md ๐Ÿ“„ docs/TESTS.md ๐Ÿ“„ docs/ADVANCED_ARCHITECTURE_GUIDE.md

About

A clean, minimal ECS-driven Pygame demo showcasing responsive UI, scene management, animations, and event-based architecture.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages