Files
Untitled-Maze-Game/README.md
2026-05-10 21:53:31 +09:00

13 KiB
Raw Blame History

Untitled Maze Game - ID30011 Midterm Project

A 3D First-Person Time-Attack Maze Game with Progressive Difficulty


Project Information


Game Overview

Concept

Untitled Maze Game is a 3D first-person maze escape game built with Babylon.js and p5.js. Players must navigate procedurally-generated mazes in a race against time, collecting a key and finding the exit within 60 seconds, but there's a twist! You must not open a chest that you have already opened before! Each successful round increases difficulty—maze size grows, more chests appear, and players advance to the next level. If you fail... there is a little surprising waiting for you.

Gameplay Flow

START SCREEN (img_start.png)
         ↓ Press R
    GAMEPLAY (60 seconds)
    ↙ Time Up / Found Exit ↖
GAME OVER          NEXT LEVEL
(img_jobapplication.png + Particles)

How to Play

  1. Start the Game: Press R on the start screen to begin
  2. Navigate: Use WASD to move, mouse to look around
  3. Find the Key: Left-Click chests until you find the key, do not click on a chest you have opened before.
  4. Reach the Exit: Once you have the key, reach the exit door
  5. Survive the Time: You have 60 seconds per round. Time runs out = Game Over
  6. Progress: Successfully exiting unlocks the next level with a larger maze and more chests

Controls

Key Action
W/A/S/D Move forward/left/backward/right
Mouse Look around (first-person)
Left Click Open a highlighted chest
R Start game (from start screen) or restart (from game-over screen)
B (For debugging purposes) Toggle debug panel (hidden by default)
V (For debugging purposes) Switch camera mode (first-person ↔ overview)
ESC Exit pointer lock

Code Documentation

Packages Used

  • 3D Graphics: Babylon.js v9.5.1 (3D scene, camera, meshes, rendering)
  • Bundler: Vite v8.0.10 (ES6 modules, asset optimization)
  • 2D Graphics: p5.js v2.x (particle effects for game-over screen)
  • Audio: Web Audio API (polyphonic sound effects)

Files Structure

src/
├── babylon_panel.js          # Core game logic, scene setup, game loop
├── html_panel.js             # UI state management (debug controls)
├── p5_particles.js           # Particle effects for start/game-over screens
└── game/
    ├── state.js              # Shared game state (config + runtime)
    ├── maze.js               # Procedural maze generation (seeded RNG)
    ├── grid.js               # Coordinate conversion and collision detection
    └── sfx.js                # Sound effect playback system

css/
└── style.css                 # Responsive layout, HUD styling, animations

img/
├── img_start.png             # Start screen background
├── img_jobapplication.png    # Game-over screen background
├── img_chest.png             # Chest texture
├── img_door.png              # Exit door texture
├── img_wall.png              # Wall texture
└── img_ground.png            # Floor texture

sfx/
├── sfx_click.wav                 # UI interaction sound
├── sfx_chest_open.wav            # Chest opening sound
├── sfx_key.wav                   # Key collection sound
├── sfx_clock.wav                 # Low-time warning alarm
├── sfx_step.wav                  # Footstep sound
├── sfx_win.wav                   # Level complete sound
├── sfx_lose.wav                  # Game over sound
└── sfx_chest_close.wav           # Chest closing sound

Core Modules

1. babylon_panel.js (Game Controller)

Responsibilities:

  • Babylon.js scene initialization and rendering loop
  • Maze generation and spatial layout
  • Game state machine (start → gameplay → game-over → restart)
  • Collision detection (camera + exit plane, chests)
  • Input handling (keyboard, mouse click)
  • Pointer lock for immersive camera control
  • Camera switching (first-person ↔ overview)

Key Functions:

  • generateLevel() - Creates maze with difficulty scaling
  • scene.registerBeforeRender() - Main game loop (countdown timer, collision checks, HUD updates)
  • showGameOverScreen() / hideGameOverScreen() - Screen transitions
  • toggleControlsPanel() - Debug UI visibility

Difficulty Scaling Formula:

roundScale = Math.max(0, level - 1)
mazeWidth = 11 + roundScale * 2
mazeHeight = 11 + roundScale * 2
chestCount = 2 + roundScale

2. game/state.js (Shared State)

Pattern: Single source of truth for game configuration and runtime state

window.mazeGameState = {
  config: {
    seed,                    // Random seed for reproducible mazes
    level,                   // Current progression level
    mazeWidth: 11,           // Base maze width (increases per level)
    mazeHeight: 11,          // Base maze height
    minChestDeadEnds: 2      // Minimum chests to place
  },
  runtime: {
    runActive: boolean,      // Is gameplay active?
    hasKey: boolean,         // Does player have the key?
    roundsCompleted: number, // Levels completed this run
    elapsedSeconds: number,  // Countdown timer (60 → 0)
    message: string          // Status text for HUD
  }
}

3. game/maze.js (Procedural Generation)

Algorithm: Recursive backtracking with seeded random number generator

Features:

  • Deterministic maze generation (same seed = same maze)
  • Configurable width/height
  • Guaranteed solvable layout
  • Dead-end detection for chest placement
  • Exportable cell data for spatial queries

4. p5_particles.js (Visual Effects)

Purpose: Full-screen particle animations on start and game-over screens

Start Screen:

  • Static full-screen display of img_start.png
  • Responsive canvas sizing

Game-Over Screen:

  • Background image (img_jobapplication.png) at full resolution
  • 15 animated particles with physics:
    • Gravity (0.1 px/frame²)
    • Wall bouncing with friction (0.8×)
    • Random rotation and direction changes
    • Semi-transparent rendering (tint: 255, alpha: 200)
    • Velocity constraints (-3 to 3 px/frame)

5. html_panel.js (Debug UI)

Responsibilities:

  • Event listeners for debug buttons (Start, Restart, Randomize seed)
  • HUD status display updates
  • Null safety checks for missing DOM elements

Note: Debug controls panel is hidden by default (press B to toggle)


Key Features & Implementation Details

Feature Implementation Status
3D Rendering Babylon.js UniversalCamera, procedural mesh generation ✓ Complete
Procedural Mazes Seeded random generation with configurable dimensions ✓ Complete
Time-Attack Mode 60-second countdown timer with auto-game-over on timeout ✓ Complete
Progressive Difficulty Maze size & chest count increase per completed level ✓ Complete
Collision Detection Raycasting for chest interaction, sphere collision for exit ✓ Complete
Sound System 8 polyphonic SFX with Web Audio API and context priming ✓ Complete
Particle Effects p5.js animated particles with physics on game-over screen ✓ Complete
Start Screen Full-screen p5.js panel with img_start.png background ✓ Complete
Game-Over Screen Full-screen overlay with job application image + particles ✓ Complete
Visual Warnings Red pulsing timer + clock sound when time < 10 seconds ✓ Complete
Camera Modes First-person (WASD + mouse) and overhead (overview) ✓ Complete
Responsive Layout Full-screen canvas with bottom-overlay debug controls ✓ Complete

🔧 Technical Highlights

Browser Compatibility:

  • Audio context requires user interaction priming via primeSfx()—automatically triggered on first W/A/S/R key press

Performance Optimizations:

  • Vite's ES6 module bundling and tree-shaking
  • Asset optimization (textures, audio)
  • Efficient raycasting for chest targeting (not per-pixel)
  • Single draw call per maze (not per cell)

Code Quality Patterns:

  • Shared State Pattern: window.mazeGameState prevents data coupling across modules
  • Factory Pattern: generateLevel() creates and caches mesh instances
  • Observer Pattern: registerBeforeRender() for frame-synchronized updates
  • Async/Await: p5.js async image loading for non-blocking resource loading

Known Issues & Limitations

⚠️ Known Behavior

  1. Chest Memory: Once a chest is opened, its visual state persists for the remainder of the game session (not reset per level)
    • Workaround: Intentional game design—players learn chest locations
  2. Pointer Lock Exit: Pressing ESC exits pointer lock but doesn't pause gameplay
    • Workaround: Intentional—players can exit anytime (no pause feature)
  3. Chunk Size Warning: Built JavaScript is ~9.1 MB due to image assets (job application image is 3MB)
    • Workaround: Not an issue for local play; consider asset compression for production

Special Features to Note

  • Seeded Randomization: Players can use the same seed to replay identical mazes (debug button: "Randomize seed")
  • Low-Time Audio Feedback: Clock sound triggers once when time drops below 10 seconds (prevents spam)
  • Full-Screen Transitions: Start screen, gameplay, and game-over have full-screen p5.js overlays for immersive presentation
  • Difficulty Scaling Formula: Mathematically designed to keep progression challenging but fair

Design Decisions & Rationale

1. Separate State Module

Decision: Isolate game state in game/state.js rather than scatter variables globally

Rationale:

  • Prevents tight coupling between UI, physics, and rendering systems
  • Enables hot-reloading during development
  • Simplifies debugging (single place to inspect game state)

2. Babylon.js Over Three.js

Decision: Used Babylon.js for 3D graphics

Rationale:

  • Built-in collision detection and raycasting
  • Superior camera controls (UniversalCamera with pointer lock)
  • Efficient mesh instancing for maze cells
  • Excellent documentation for procedural generation

3. p5.js for Particle Effects

Decision: Delegated particle rendering to p5.js instead of Babylon.js

Rationale:

  • Cleaner separation of concerns (game logic ≠ visual effects)
  • p5.js's simple drawing API reduces code complexity
  • Easy to swap/experiment with particle physics without affecting core game
  • Full-screen 2D canvas doesn't compete with 3D rendering pipeline

4. Time-Attack Mode (vs. Exploration, Level Editing)

Decision: 60-second countdown instead of unlimited time

Rationale:

  • Creates urgency and strategic decision-making (pick efficient paths vs. explore)
  • Enables meaningful progression (faster times unlock harder mazes)
  • Reduces scope (no need for complex AI, item management, etc.)

Help from AI & Resources

AI Assistance

  • GitHub Copilot: Used for code structure review and refactoring suggestions
    • Suggested separating static maze data from dynamic game state (instead of coupling both in a single 2D array)
    • Helped organize modules into logical file structure
    • Reviewed p5.js particle physics for correctness

Resources & Documentation

  • Babylon.js Playground: Reference for collision detection and camera control
  • p5.js Documentation: Async setup pattern for p5.js 2.0+ (no preload())
  • MDN Web Audio API: Context priming for cross-browser audio compatibility
  • Vite Documentation: Asset bundling and import resolution

Game Design Inspiration

  • Portal series: Puzzle-based spatial reasoning and exit goals
  • Deus Ex: Multiple approaches to objectives (fast routing vs. exploration)
  • Pac-Man: Time pressure and progressive difficulty

Conclusion

Untitled Maze Game demonstrates:

  • Professional code organization (modular, well-separated concerns)
  • Advanced 3D graphics programming (procedural generation, collision detection, camera control)
  • Full-featured game loop with state management
  • Polish and presentation (particle effects, sound design, responsive UI)
  • Scalability (difficulty scaling formula, asset management)

The codebase prioritizes readability and maintainability through modular design, clear naming conventions, and comprehensive documentation. Each file has a single responsibility, making it easy for collaborators or reviewers to understand and extend the code.