Architecture¶
This document provides an overview of the brkrs game architecture.
High-Level Design¶
brkrs is built on the Bevy game engine using an Entity-Component-System (ECS) architecture with Rapier3D for physics simulation.
┌─────────────────────────────────────────────────────────────┐
│ Game Application │
├─────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Input │ │ Level │ │ Physics │ │ UI │ │
│ │ System │ │ Loader │ │ (Rapier) │ │ System │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │
│ └─────────────┴──────┬──────┴─────────────┘ │
│ │ │
│ ┌───────┴───────┐ │
│ │ Bevy ECS │ │
│ │ (Entities, │ │
│ │ Components, │ │
│ │ Resources) │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────────────────┘
Core Principles¶
The project follows these architectural principles (from the project Constitution):
ECS-First: All game logic uses Bevy’s ECS paradigm
Physics-Driven: Gameplay relies on Rapier3D for collisions and movement
Modular Features: Each feature is independently testable
Performance-First: 60 FPS target on native and WASM
Cross-Platform: Supports native (Linux/Windows/macOS) and WASM
Rustdoc: Public APIs are documented (intent-focused)
TDD-First: Tests are written first, fail (red), and are approved before implementation
Bevy 0.17 mandates: Follow Bevy 0.17 ECS/graphics/performance rules and prohibitions
System Overview¶
Core Systems¶
System |
Purpose |
Location |
|---|---|---|
Level Loader |
Parses RON files, spawns entities |
|
Pause System |
Freezes physics, shows overlay |
|
Respawn |
Ball respawn after loss |
|
Level Switch |
Transitions between levels |
|
Scoring |
Tracks points, awards milestone bonuses |
|
Audio System |
Plays sound effects for collisions, level transitions, milestones |
|
Cheat Mode |
Developer/testing feature for quick level exploration |
|
Paddle Size |
Handles paddle size powerup effects (shrink/enlarge) |
|
Multi-Hit Bricks |
Manages multi-hit brick durability and transitions |
|
Textures |
Loads and manages textures, per-level material overrides |
|
Grid Debug |
Development visualization |
|
Spawning |
Initial scene setup (camera, light, ground) |
|
Component Structure¶
Entities use centralized physics configuration resources instead of hardcoded values:
Entity: Ball
├── Transform
├── RigidBody (Dynamic)
├── Collider (Sphere)
├── Velocity
├── Restitution (from BallPhysicsConfig.restitution)
├── Friction (from BallPhysicsConfig.friction)
├── Damping (from BallPhysicsConfig.linear_damping/angular_damping)
└── Ball (marker component)
Entity: Paddle
├── Transform
├── RigidBody (Kinematic)
├── Collider (Box)
├── Restitution (from PaddlePhysicsConfig.restitution)
├── Friction (from PaddlePhysicsConfig.friction)
└── Paddle (marker component)
Entity: Brick
├── Transform
├── RigidBody (Fixed)
├── Collider (Box)
├── Restitution (from BrickPhysicsConfig.restitution)
├── Friction (from BrickPhysicsConfig.friction)
├── Brick (marker component)
└── [Optional] Indestructible
Entity: Merkaba (Hazard)
├── Transform
├── RigidBody (Dynamic)
├── Collider (Cylinder)
├── CollisionGroups (Group 2, ALL)
├── SolverGroups (Group 2, ALL ^ Group 1)
├── Velocity
└── Merkaba (marker component)
Physics Configuration Resources:
BallPhysicsConfig— Ball physics properties (restitution, friction, damping)PaddlePhysicsConfig— Paddle physics propertiesBrickPhysicsConfig— Brick physics properties
All configs include validation methods to ensure physics values are reasonable and prevent runtime errors.
State Machine¶
┌─────────────┐
│ Menu │ (planned)
└──────┬──────┘
│ Start Game
▼
┌─────────────┐
┌───►│ Playing │◄───┐
│ └──────┬──────┘ │
│ │ ESC │ Click
│ ▼ │
│ ┌─────────────┐ │
│ │ Paused │────┘
│ └─────────────┘
│
│ Level Complete
└────────────────────
Physics Architecture¶
Coordinate System¶
Bevy Convention: Right-handed Y-up coordinate system
Y-axis: Vertical (up/down), locked for gameplay entities
X-axis: Lateral movement (left/right)
Z-axis: Forward/backward movement (+Z toward camera, -Z into screen)
Game Implementation: Top-down view with movement on XZ plane
Camera positioned at Y=37 looking down at origin
Entities use
LockedAxes::TRANSLATION_LOCKED_Yto constrain movement to XZ planeFrom player perspective: +Z = forward (toward goal/bricks), -Z = backward (toward paddle)
Gameplay “forward” refers to +Z direction, not Bevy’s
Transform::forward()API (-Z)
Plane Constraint¶
All gameplay occurs on the XZ horizontal plane at Y=2.0:
Entities use
LockedAxes::TRANSLATION_LOCKED_YCamera positioned above at Y=37, looking down
3D rendering provides depth and shadows
Collision Handling¶
Ball ──collision──► Brick
│
▼
Check Indestructible?
/ \
No Yes
│ │
▼ ▼
Destroy Bounce
Brick Only
Paddle Physics¶
Mouse movement controls paddle position
Mouse scroll rotates the paddle
Recent mouse velocity applies “english” to ball on contact
Level System¶
Loading Flow¶
assets/levels/level_001.ron
│
▼
LevelDefinition
(RON parsing)
│
▼
Entity Spawning
(per cell in matrix)
│
▼
Physics Setup
(colliders, rigid bodies)
Level Transitions¶
Clear current level entities (except camera, UI)
Parse new level file
Spawn new entities
Reset ball/paddle positions if needed
Game State Resources¶
Scoring System¶
The scoring system tracks cumulative points throughout a game session:
Brick Destroyed
|
v
BrickDestroyed (message)
|
v
award_points_system
|
v
ScoreState (resource)
- current_score: u32
- last_milestone_reached: u32
|
v
detect_milestone_system
|
v (if milestone crossed)
MilestoneReached (message)
|
v
award_milestone_ball_system
|
v
LivesState.lives_remaining += 1
Point Values: Defined in docs/bricks.md, ranging from 25-300 points per brick
Milestones: Every 5000 points awards an extra life
Special Cases:
Question brick (53): Random 25-300 points
Extra Ball brick (41): 0 points (grants life via separate mechanism)
Magnet bricks (55-56): 0 points (effect-only)
Persistence: Score accumulates across level transitions, resets on game restart
Messages vs Observers (Bevy 0.17+)
See the constitution’s “Bevy 0.17 Event, Message, and Observer Clarification” for authoritative guidance.
Messages (
#[derive(Message)]) are for double-buffered, frame-agnostic data streams (e.g., scoring, telemetry). Produced viaMessageWriter, consumed viaMessageReader. Use for work that can be batched or delayed to the next schedule step. Not for immediate side-effects.Observers (with
#[derive(Event)],On<T>,Trigger<T>, or observer systems) are for immediate or next-frame reactions (e.g., UI, sound, spawning). Use for real-time, reactive logic that needs full system access and instant feedback.
Key rules:
Use Messages for batchable, cross-frame work; Observers for instant, reactive logic.
Never create observer systems that listen to Messages; only Events/Triggers are valid for observers.
Always justify your choice in specs/plans (see constitution for rationale and examples).
UI System¶
Pause Overlay¶
The pause overlay is a separate UI layer that:
Appears on ESC press
Freezes physics simulation
Shows resume instruction
Dismisses on mouse click
State Handling¶
GameState::Playing
│ ESC
▼
GameState::Paused
│
├── Hide cursor (native)
├── Freeze physics
└── Show overlay
│
│ Click
▼
GameState::Playing
│
├── Show cursor
├── Resume physics
└── Hide overlay
Cross-Platform Considerations¶
WASM Differences¶
Feature |
Native |
WASM |
|---|---|---|
Window mode switching |
✓ Fullscreen toggle |
✗ Not supported |
Audio |
Full support |
Web Audio API |
File I/O |
Direct |
Embedded assets |
Performance |
Full speed |
~60-80% native |
Asset Embedding¶
For WASM builds, assets are embedded at compile time. The build process:
Compiles to
wasm32-unknown-unknowntargetRuns
wasm-bindgenfor JS interopAssets bundled into the WASM binary
Further Reading¶
Developer Guide for development setup
Contributing for code contribution guidelines