307 lines
13 KiB
Markdown
307 lines
13 KiB
Markdown
# 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.
|
||
|