# Untitled Maze Game - ID30011 Midterm Project **A 3D First-Person Time-Attack Maze Game with Progressive Difficulty** --- ## Project Information - **Name:** Bumgyu Suh - **Student ID:** 20240905 - **Student Email:** bumgyu@kaist.ac.kr - **Repository URL:** https://git.prototyping.id/20240905/Untitled-Maze-Game - **Video Demonstration:** [YouTube Link] --- ## 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 ```javascript 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.