Architecture Overview
The Procedural Pac-Man 3D game follows a hierarchical architecture built on Three.js, organizing code into distinct layers that handle rendering, game logic, and component management.
Core Architecture Pattern
The game uses a layered architecture with clear separation of concerns:
MyScene (Rendering Layer)
↓
MyGame (Game Logic Layer)
↓
Components (Maze, Characters, UI)
Component Hierarchy
MyScene extends THREE.Scene
├── Renderer (WebGLRenderer)
├── Cameras (freeCam, topCam, sideCam)
├── Lights (AmbientLight, SpotLight)
├── MyGame extends THREE.Object3D
│ ├── MyMaze (Procedural maze generation)
│ ├── Characters (Pacman + 4 Ghosts)
│ ├── Score & Level UI
│ └── Controls Display
└── MyMenu (Start screen)
The architecture uses Three.js’s scene graph structure, where each component extends THREE.Object3D for automatic transform hierarchy and rendering.
Three.js Rendering Pipeline
The game leverages Three.js’s rendering pipeline with a custom shader material for the maze walls:
Initialization Flow
// From MyScene.js:2-46
class MyScene extends THREE . Scene {
constructor ( myCanvas ) {
super ();
// 1. Create renderer
this . renderer = this . createRenderer ( myCanvas );
// 2. Setup lighting
this . createLights ();
// 3. Setup cameras
this . createCamera ();
// 4. Initialize game logic
this . game = new MyGame ( this . topCam );
// 5. Create menu
this . menu = new MyMenu ();
this . add ( this . menu );
}
}
Rendering Configuration
The renderer is configured for full-screen rendering with a black background:
// From MyScene.js:105-121
createRenderer ( myCanvas ) {
var renderer = new THREE . WebGLRenderer ();
renderer . setClearColor ( new THREE . Color ( 0x000000 ), 1.0 );
renderer . setSize ( window . innerWidth , window . innerHeight );
$ ( myCanvas ). append ( renderer . domElement );
return renderer ;
}
The game uses jQuery for DOM manipulation, appending the Three.js canvas to a #WebGL-output div element.
Main Game Loop Pattern
The game implements a continuous update loop using requestAnimationFrame:
// From MyScene.js:217-235
update () {
// Request next frame
requestAnimationFrame (() => this . update ());
// Update camera controls
this . cameraControl . update ();
// Update game objects
this . game . title . lookAt ( this . getCamera (). position );
this . game . maze . update ();
// Update based on game state
if ( this . status == "PACMAN" ) this . game . update ();
else if ( this . status == "MENU" ) this . menu . update ();
// Render the scene
this . renderer . render ( this , this . getCamera ());
}
Game State Management
The game uses simple string-based state management:
"MENU" - Initial state showing the start menu
"PACMAN" - Active gameplay state
State transitions occur through user interactions (clicking the play button).
Component Communication
Components communicate through direct references and method calls :
MyScene → MyGame
// From MyScene.js:206-212
onMouseClick ( event ) {
if ( pickedObjects . length > 0 ) {
this . remove ( this . menu );
this . game . startGame (); // Direct method call
this . add ( this . game );
this . status = "PACMAN" ;
this . changeCamera ( 2 );
}
}
MyGame → Components
// From MyGame.js:330-344
update () {
if ( this . start && this . characters [ 0 ]. status == "alive" ) {
this . moveAI (); // Update ghost AI
this . collisionManager (); // Check collisions
this . controlTile (); // Check tile interactions
if ( this . maze . getDotNumber () == 0 ) {
this . nextLevel (); // Level progression
}
}
TWEEN . update (); // Update animations
}
The game uses the TWEEN.js library for smooth animations, particularly for ghost behavior transitions.
Key Design Patterns
1. Composition over Inheritance
All game objects extend THREE.Object3D, composing functionality through contained objects rather than deep inheritance hierarchies.
2. Procedural Generation
The maze is generated procedurally using a Tetris-piece-based algorithm, creating unique layouts each game:
// From MyMaze.js:387-483
mazeGenerator () {
var tetris3 = this . tetris3x3Generator ();
// Mirrors tetris layout for symmetry
// Adds portals and fills with dots
// Returns 30x30 maze data
}
3. Data-Driven Rendering
The maze structure is stored as a 2D array, where each cell type determines rendering:
0 = Wall (with shader material)
1 = Empty space
2 = Dot (collectible)
3 = Power pill
4 = Teleport portal
Input handling is centralized in MyScene through event listeners:
// From MyScene.js:239-252
$ ( function () {
var scene = new MyScene ( "#WebGL-output" );
window . addEventListener ( "resize" , () => scene . onWindowResize ());
window . addEventListener ( "keydown" , ( event ) => scene . onKeyDown ( event ));
window . addEventListener ( "mousemove" , ( event ) => scene . onMouseMove ( event ));
window . addEventListener ( "click" , ( event ) => scene . onMouseClick ( event ));
scene . update ();
});
Efficient Collision Detection
Collision detection is optimized by only checking adjacent tiles:
// From MyMaze.js:583-611
checkCollision ( hitbox , pos , dir ) {
// Only check 3 tiles in movement direction
for ( var i = pos_aux - 1 ; i <= pos_aux + 1 && ! collision ; i ++ ) {
// Check intersection with tile hitbox
}
}
Object Pooling
Characters are respawned rather than recreated, reducing garbage collection:
// From MyGame.js:277-289
respawn () {
this . leaveBoxAnimation . stop ();
for ( var i = 0 ; i < 5 ; i ++ ) {
this . remove ( this . characters [ 0 ]);
this . characters [ 0 ]. dispose ();
this . characters . shift ();
}
this . createCharacters ();
}
The game includes an FPS counter via stats.js for performance monitoring during development.
External Dependencies
Core Libraries
Three.js - 3D rendering engine
jQuery - DOM manipulation
TrackballControls - Camera controls for free camera
TWEEN.js - Animation library
astar.js - A* pathfinding for ghost AI
dat.GUI - Debug GUI (optional)
Custom Shaders
The game includes custom GLSL shaders for procedural Voronoi patterns on maze walls, defined inline in index.html:57-134.
Next Steps
Scene Structure Learn about cameras, lights, and scene organization
Game Loop Deep dive into the update cycle and game logic