Overview
MyGhost extends MyCharacter to create enemy ghosts with pathfinding, multiple behavioral states (chase, scared, return home), visual effects, and animations.
Inheritance: THREE.Object3D → MyCharacter → MyGhost
Constructor
Initial position of the ghost in grid coordinates
Base size of the ghost model
Initial direction with x and y properties (values: -1, 0, or 1)
Material for the ghost’s body color (e.g., red, pink, cyan, orange)
const ghost = new MyGhost(
new THREE.Vector3(14, 0, 11),
1.0,
{x: 0, y: 1},
MyMaterial.RED
);
Properties
behaviour
"chase" | "scape" | "return" | "freeze" | "home"
Current AI behavior state. Default: "chase"
- chase: Actively pursuing Pac-Man
- scape: Running away (vulnerable state when Pac-Man eats power pellet)
- return: Returning to home position after being eaten
- freeze: Not moving
- home: Waiting in home area
updatePathTime
Milliseconds between path recalculations during chase mode. Default: 7500
scareTime
Duration in milliseconds that scared state lasts. Default: 10000
speed
Movement speed (automatically set to 75% of MyCharacter base speed). Modified based on behavior.
path
Current pathfinding route as array of grid positions. Format: [{x: row, y: col}, ...]
sphereGeom
Geometry for the ghost’s rounded head (hemisphere, 4/5 size).
eyeGeom
Geometry for the white eyeball spheres (6/25 size).
pupilGeom
Geometry for the black pupils (2/25 size).
cylinderGeom
Geometry for the ghost’s cylindrical body (4/5 radius, 4/3 height).
material
Current material applied to the ghost’s body (changes with behavior state).
cylinder
Cylindrical body mesh.
sphere
Hemisphere head mesh.
rightEye
Container for right eyeball and pupil.
leftEye
Container for left eyeball and pupil.
eyes
Container for both eyes, positioned to face forward.
feetGeom
Geometry for the wavy bottom skirt (16 triangular segments).
feet
Container with 16 rotated foot meshes creating the wavy skirt effect.
Methods
executePath()
Follows the current pathfinding route. Moves toward the next waypoint and rotates accordingly. Called automatically by update().
// Set a path and the ghost will follow it
ghost.path = pathfindingAlgorithm(ghost.position, target.position);
scare()
Transitions ghost to scared state: changes to blue color, starts invincibility timer, and clears current path.
// Pac-Man ate a power pellet
powerPellets.forEach(pellet => {
if (pellet.eaten) {
ghosts.forEach(ghost => ghost.scare());
}
});
revive()
Returns ghost to chase behavior after scared timer expires. Halves movement speed temporarily.
// Called automatically when scare animation completes
chase()
Sets ghost to chase behavior: restores original color, clears path, and begins pursuing Pac-Man.
// Return to normal behavior
ghost.chase();
returnHome()
Initiates return to home position after being eaten: makes ghost invisible, doubles speed, and clears path.
// Pac-Man ate the ghost while scared
if (collision && ghost.behaviour === "scape") {
ghost.returnHome();
score += 200;
}
changeColor(material)
Updates the ghost’s body material (affects cylinder, sphere, and feet).
Parameters:
material (THREE.Material) - New material to apply
// Make ghost blue when scared
ghost.changeColor(MyMaterial.BLUE);
// Make ghost invisible when returning home
ghost.changeColor(MyMaterial.INVISIBLE);
changeBehaviour(status)
Directly sets the behavior state.
Parameters:
status (string) - New behavior: “chase”, “scape”, “return”, “freeze”, or “home”
// Freeze ghost during game pause
ghost.changeBehaviour("freeze");
// Resume chasing
ghost.changeBehaviour("chase");
startUpdatingPaths()
Begins periodic path recalculation timer. Clears path every updatePathTime milliseconds during chase mode.
// Start the ghost AI
ghost.startUpdatingPaths();
stopUpdatingPaths()
Stops the path recalculation timer.
// Pause ghost AI
ghost.stopUpdatingPaths();
setUpdatePathTime(time)
Changes how frequently paths are recalculated.
Parameters:
time (number) - Milliseconds between path updates
// Make ghost more reactive (recalculate every 3 seconds)
ghost.setUpdatePathTime(3000);
// Make ghost less reactive (recalculate every 15 seconds)
ghost.setUpdatePathTime(15000);
setScareTime(time)
Changes duration of scared state.
Parameters:
time (number) - Milliseconds to stay scared
// Shorter vulnerability period
ghost.setScareTime(5000);
startInvencibleAnimation()
Plays the flashing animation sequence when scared: stays blue for scareTime duration, then flashes blue/white 10 times over 5 seconds before returning to chase.
// Called automatically by scare()
stopInvencibleAnimation()
Stops the scared animation timers.
// Called automatically by returnHome()
dispose()
Cleans up geometries to free memory.
// Before removing ghost from scene
ghost.dispose();
scene.remove(ghost);
update()
Updates ghost state each frame: executes pathfinding, moves, and updates animations. Does nothing if behavior is “freeze” or “home”.
// In game loop
function animate() {
ghost.update();
requestAnimationFrame(animate);
}
Usage Example
// Create four ghosts with different colors
const blinky = new MyGhost(
new THREE.Vector3(14, 0, 11),
1.0,
{x: 0, y: 1},
MyMaterial.RED
);
const pinky = new MyGhost(
new THREE.Vector3(14, 0, 13),
1.0,
{x: 0, y: -1},
MyMaterial.PINK
);
const inky = new MyGhost(
new THREE.Vector3(12, 0, 13),
1.0,
{x: 1, y: 0},
MyMaterial.CYAN
);
const clyde = new MyGhost(
new THREE.Vector3(16, 0, 13),
1.0,
{x: -1, y: 0},
MyMaterial.ORANGE
);
const ghosts = [blinky, pinky, inky, clyde];
ghosts.forEach(ghost => {
scene.add(ghost);
ghost.startUpdatingPaths();
});
// Game loop with AI pathfinding
function gameLoop() {
ghosts.forEach(ghost => {
// Calculate new path if needed
if (ghost.path === null && ghost.behaviour === "chase") {
const ghostPos = getGridPosition(ghost);
const pacmanPos = getGridPosition(pacman);
ghost.path = findPath(maze, ghostPos, pacmanPos);
}
// Calculate return home path
if (ghost.path === null && ghost.behaviour === "return") {
const ghostPos = getGridPosition(ghost);
const homePos = {x: 14, y: 13};
ghost.path = findPath(maze, ghostPos, homePos);
}
// Calculate escape path (away from Pac-Man)
if (ghost.path === null && ghost.behaviour === "scape") {
const ghostPos = getGridPosition(ghost);
const pacmanPos = getGridPosition(pacman);
ghost.path = findEscapePath(maze, ghostPos, pacmanPos);
}
// Update ghost
ghost.update();
// Check collision with Pac-Man
if (checkCollision(ghost, pacman)) {
if (ghost.behaviour === "scape") {
// Ghost is vulnerable - send it home
ghost.returnHome();
score += 200;
} else if (ghost.behaviour === "chase") {
// Pac-Man dies
pacman.die();
}
}
});
requestAnimationFrame(gameLoop);
}
// Handle power pellet collection
function onPowerPelletEaten() {
ghosts.forEach(ghost => {
if (ghost.behaviour === "chase") {
ghost.scare();
}
});
}
Behavior State Diagram
[START]
↓
[chase] ←――――――――――――┐
↓ │
Power Pellet Eaten │
↓ │
[scape] revive()
↓ │
Collision with Pac-Man │
↓ │
[return] │
↓ │
Reach home ――――――――――――┘
Visual Effects
Hover Animation
- Duration: 1 second per cycle
- Range: ±0.35 units vertical
- Type: Yoyo with infinite repeat
- Effect: Gentle bobbing motion
Scared Animation Sequence
- Phase 1 (0-10 seconds): Solid blue color
- Phase 2 (10-15 seconds): Flashing blue/white every 0.5 seconds (10 flashes)
- End: Returns to chase behavior with original color
Notes
- Ghost speed is 75% of base character speed, making them slightly slower than Pac-Man
- When returning home after being eaten, speed doubles and ghost becomes invisible
- Eyes always face forward in the direction of movement
- The wavy skirt is created using 16 extruded triangular segments
- Path recalculation frequency affects difficulty: shorter intervals make ghosts more responsive
- Each ghost typically uses a different pathfinding strategy in actual Pac-Man (not implemented in this base class)