6-space-game/4-collision-detection/README.md
journey
title Your Collision Detection Journey
section Physics Foundation
Understand rectangles: 3: Student
Learn intersection math: 4: Student
Grasp coordinate systems: 4: Student
section Game Mechanics
Implement laser firing: 4: Student
Add object lifecycle: 5: Student
Create collision rules: 5: Student
section System Integration
Build collision detection: 5: Student
Optimize performance: 5: Student
Test interaction systems: 5: Student
Think about the moment in Star Wars when Luke's proton torpedoes hit the Death Star's exhaust port. That precise collision detection changed the fate of the galaxy! In games, collision detection works the same way - it determines when objects interact and what happens next.
In this lesson, you'll add laser weapons to your space game and implement collision detection. Just like NASA's mission planners calculate spacecraft trajectories to avoid debris, you'll learn to detect when game objects intersect. We'll break this down into manageable steps that build on each other.
By the end, you'll have a functioning combat system where lasers destroy enemies and collisions trigger game events. These same collision principles are used in everything from physics simulations to interactive web interfaces.
mindmap
root((Collision Detection))
Physics Concepts
Rectangle Boundaries
Intersection Testing
Coordinate Systems
Separation Logic
Game Objects
Laser Projectiles
Enemy Ships
Hero Character
Collision Zones
Lifecycle Management
Object Creation
Movement Updates
Destruction Marking
Memory Cleanup
Event Systems
Keyboard Input
Collision Events
Game State Changes
Audio/Visual Effects
Performance
Efficient Algorithms
Frame Rate Optimization
Memory Management
Spatial Partitioning
ā Do a little research on the very first computer game ever written. What was its functionality?
Collision detection works like the proximity sensors on the Apollo lunar module - it constantly checks distances and triggers alerts when objects get too close. In games, this system determines when objects interact and what should happen next.
The approach we'll use treats every game object as a rectangle, similar to how air traffic control systems use simplified geometric shapes to track aircraft. This rectangular method might seem basic, but it's computationally efficient and works well for most game scenarios.
Every game object needs coordinate boundaries, similar to how the Mars Pathfinder rover mapped its location on the Martian surface. Here's how we define these boundary coordinates:
flowchart TD
A["šÆ Game Object"] --> B["š Position (x, y)"]
A --> C["š Dimensions (width, height)"]
B --> D["Top: y"]
B --> E["Left: x"]
C --> F["Bottom: y + height"]
C --> G["Right: x + width"]
D --> H["š² Rectangle Bounds"]
E --> H
F --> H
G --> H
H --> I["Collision Detection Ready"]
style A fill:#e3f2fd
style H fill:#e8f5e8
style I fill:#fff3e0
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width
}
}
Let's break this down:
Detecting rectangle intersections uses logic similar to how the Hubble Space Telescope determines if celestial objects are overlapping in its field of view. The algorithm checks for separation:
flowchart LR
A["Rectangle 1"] --> B{"Separation Tests"}
C["Rectangle 2"] --> B
B --> D["R2 left > R1 right?"]
B --> E["R2 right < R1 left?"]
B --> F["R2 top > R1 bottom?"]
B --> G["R2 bottom < R1 top?"]
D --> H{"Any True?"}
E --> H
F --> H
G --> H
H -->|Yes| I["ā No Collision"]
H -->|No| J["ā
Collision Detected"]
style B fill:#e3f2fd
style I fill:#ffebee
style J fill:#e8f5e8
function intersectRect(r1, r2) {
return !(r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top);
}
The separation test works like radar systems:
If none of these conditions are true, the rectangles must be overlapping. This approach mirrors how radar operators determine if two aircraft are at safe distances.
When a laser hits an enemy, both objects need to be removed from the game. However, deleting objects mid-loop can cause crashes - a lesson learned the hard way in early computer systems like the Apollo Guidance Computer. Instead, we use a "mark for deletion" approach that safely removes objects between frames.
stateDiagram-v2
[*] --> Active: Object Created
Active --> Collided: Collision Detected
Collided --> MarkedDead: Set dead = true
MarkedDead --> Filtered: Next Frame
Filtered --> [*]: Object Removed
Active --> OutOfBounds: Leaves Screen
OutOfBounds --> MarkedDead
note right of MarkedDead
Safe to continue
current frame
end note
note right of Filtered
Objects removed
between frames
end note
Here's how we mark something for removal:
// Mark object for removal
enemy.dead = true;
Why this approach works:
Then filter out marked objects before the next render cycle:
gameObjects = gameObjects.filter(go => !go.dead);
What this filtering does:
Laser projectiles in games work on the same principle as photon torpedoes in Star Trek - they're discrete objects that travel in straight lines until they hit something. Each spacebar press creates a new laser object that moves across the screen.
To make this work, we need to coordinate a few different pieces:
Key components to implement:
Unlimited firing rates would overwhelm the game engine and make gameplay too easy. Real weapon systems face similar constraints - even the USS Enterprise's phasers needed time to recharge between shots.
We'll implement a cooldown system that prevents rapid-fire spamming while maintaining responsive controls:
sequenceDiagram
participant Player
participant Weapon
participant Cooldown
participant Game
Player->>Weapon: Press Spacebar
Weapon->>Cooldown: Check if cool
alt Weapon is Ready
Cooldown->>Weapon: cool = true
Weapon->>Game: Create Laser
Weapon->>Cooldown: Start new cooldown
Cooldown->>Cooldown: cool = false
Note over Cooldown: Wait 500ms
Cooldown->>Cooldown: cool = true
else Weapon is Cooling
Cooldown->>Weapon: cool = false
Weapon->>Player: No action
end
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time);
}
}
class Weapon {
constructor() {
this.cooldown = null;
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// Create laser projectile
this.cooldown = new Cooldown(500);
} else {
// Weapon is still cooling down
}
}
}
How the cooldown works:
ā Refer to lesson 1 in the space game series to remind yourself about cooldowns.
You'll extend your existing space game code to create a collision detection system. Like the International Space Station's automated collision avoidance system, your game will continuously monitor object positions and respond to intersections.
Starting from your previous lesson's code, you'll add collision detection with specific rules that govern object interactions.
š” Pro Tip: The laser sprite is already included in your assets folder and referenced in your code, ready for implementation.
Game mechanics to add:
Collision Detection Foundation: Before implementing, ensure you understand:
Quick Self-Test: What would happen if you deleted objects immediately instead of marking them? Answer: Mid-loop deletion could cause crashes or skip objects in iteration
Physics Understanding: You now grasp:
Good news - we've already set up most of the groundwork for you! All your game assets and basic structure are waiting in the your-work subfolder, ready for you to add the cool collision features.
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
Understanding the file structure:
Navigate to your project folder and start the local server:
cd your-work
npm start
This command sequence:
http://localhost:5000Open your browser and navigate to http://localhost:5000 to see your current game state with the hero and enemies rendered on screen.
Like the systematic approach NASA used to program the Voyager spacecraft, we'll implement collision detection methodically, building each component step by step.
flowchart TD
A["1. Rectangle Bounds"] --> B["2. Intersection Detection"]
B --> C["3. Laser System"]
C --> D["4. Event Handling"]
D --> E["5. Collision Rules"]
E --> F["6. Cooldown System"]
G["Object Boundaries"] --> A
H["Physics Algorithm"] --> B
I["Projectile Creation"] --> C
J["Keyboard Input"] --> D
K["Game Logic"] --> E
L["Rate Limiting"] --> F
F --> M["š® Complete Game"]
style A fill:#e3f2fd
style B fill:#e8f5e8
style C fill:#fff3e0
style D fill:#f3e5f5
style E fill:#e0f2f1
style F fill:#fce4ec
style M fill:#e1f5fe
First, let's teach our game objects how to describe their boundaries. Add this method to your GameObject class:
rectFromGameObject() {
return {
top: this.y,
left: this.x,
bottom: this.y + this.height,
right: this.x + this.width,
};
}
This method accomplishes:
Now let's create our collision detective - a function that can tell when two rectangles are overlapping:
function intersectRect(r1, r2) {
return !(
r2.left > r1.right ||
r2.right < r1.left ||
r2.top > r1.bottom ||
r2.bottom < r1.top
);
}
This algorithm works by:
false if any separation condition is trueHere's where things get exciting! Let's set up the laser firing system.
First, let's define some message types so different parts of our game can talk to each other:
KEY_EVENT_SPACE: "KEY_EVENT_SPACE",
COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER",
COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
These constants provide:
Add space key detection to your key event listener:
} else if(evt.keyCode === 32) {
eventEmitter.emit(Messages.KEY_EVENT_SPACE);
}
This input handler:
Register firing behavior in your initGame() function:
eventEmitter.on(Messages.KEY_EVENT_SPACE, () => {
if (hero.canFire()) {
hero.fire();
}
});
This event listener:
Add collision handling for laser-enemy interactions:
eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => {
first.dead = true;
second.dead = true;
});
This collision handler:
Implement a laser projectile that moves upward and manages its own lifecycle:
class Laser extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 9;
this.height = 33;
this.type = 'Laser';
this.img = laserImg;
let id = setInterval(() => {
if (this.y > 0) {
this.y -= 15;
} else {
this.dead = true;
clearInterval(id);
}
}, 100);
}
}
This class implementation:
setInterval()Create a comprehensive collision detection function:
function updateGameObjects() {
const enemies = gameObjects.filter(go => go.type === 'Enemy');
const lasers = gameObjects.filter(go => go.type === "Laser");
// Test laser-enemy collisions
lasers.forEach((laser) => {
enemies.forEach((enemy) => {
if (intersectRect(laser.rectFromGameObject(), enemy.rectFromGameObject())) {
eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, {
first: laser,
second: enemy,
});
}
});
});
// Remove destroyed objects
gameObjects = gameObjects.filter(go => !go.dead);
}
This collision system:
ā ļø Important: Add
updateGameObjects()to your main game loop inwindow.onloadto enable collision detection.
Enhance the Hero class with firing mechanics and rate limiting:
class Hero extends GameObject {
constructor(x, y) {
super(x, y);
this.width = 99;
this.height = 75;
this.type = "Hero";
this.speed = { x: 0, y: 0 };
this.cooldown = 0;
}
fire() {
gameObjects.push(new Laser(this.x + 45, this.y - 10));
this.cooldown = 500;
let id = setInterval(() => {
if (this.cooldown > 0) {
this.cooldown -= 100;
} else {
clearInterval(id);
}
}, 200);
}
canFire() {
return this.cooldown === 0;
}
}
Understanding the enhanced Hero class:
canFire() methodComplete System Understanding: Verify your mastery of the collision system:
System Integration: Your collision detection demonstrates:
Professional Patterns: You've implemented:
Your space game now features complete collision detection and combat mechanics. š Test these new capabilities:
You've successfully implemented a collision detection system using the same mathematical principles that guide spacecraft navigation and robotics.
console.log statements to track collision events in real-timetimeline
title Collision Detection & Game Physics Learning Progression
section Foundation (10 minutes)
Rectangle Math: Coordinate systems
: Boundary calculations
: Position tracking
: Dimension management
section Algorithm Design (20 minutes)
Intersection Logic: Separation testing
: Overlap detection
: Performance optimization
: Edge case handling
section Game Implementation (30 minutes)
Object Systems: Lifecycle management
: Event coordination
: State tracking
: Memory cleanup
section Interactive Features (40 minutes)
Combat Mechanics: Projectile systems
: Weapon cooldowns
: Damage calculation
: Visual feedback
section Advanced Physics (50 minutes)
Real-time Systems: Frame rate optimization
: Spatial partitioning
: Collision response
: Physics simulation
section Professional Techniques (1 week)
Game Engine Concepts: Component systems
: Physics pipelines
: Performance profiling
: Cross-platform optimization
section Industry Applications (1 month)
Production Skills: Large-scale optimization
: Team collaboration
: Engine development
: Platform deployment
After completing this lesson, you now have mastered:
Real-World Applications: Your collision detection skills apply directly to:
Professional Skills Gained: You can now:
Game Development Concepts Mastered:
Next Level: You're ready to explore advanced physics engines like Matter.js, implement 3D collision detection, or build complex particle systems!
š Achievement Unlocked: You've built a complete physics-based interaction system with professional-grade collision detection!
Use the Agent mode to complete the following challenge:
Description: Enhance the collision detection system by implementing power-ups that spawn randomly and provide temporary abilities when collected by the hero ship.
Prompt: Create a PowerUp class that extends GameObject and implement collision detection between the hero and power-ups. Add at least two types of power-ups: one that increases fire rate (reduces cooldown) and another that creates a temporary shield. Include spawn logic that creates power-ups at random intervals and positions.
Add an explosion! Take a look at the game assets in the Space Art repo and try to add an explosion when the laser hits an alien
Experiment with the intervals in your game thus far. What happens when you change them? Read more about JavaScript timing events.