6-space-game/1-introduction/README.md
journey
title Your Game Development Journey
section Foundation
Learn game architecture: 3: Student
Understand inheritance: 4: Student
Explore composition: 4: Student
section Communication
Build pub/sub system: 4: Student
Design event flow: 5: Student
Connect components: 5: Student
section Application
Create game objects: 5: Student
Implement patterns: 5: Student
Plan game structure: 5: Student
Just like NASA's mission control coordinates multiple systems during a space launch, we're going to build a space game that demonstrates how different parts of a program can work together seamlessly. While creating something you can actually play, you'll learn essential programming concepts that apply to any software project.
We'll explore two fundamental approaches to organizing code: inheritance and composition. These aren't just academic concepts ā they're the same patterns that power everything from video games to banking systems. We'll also implement a communication system called pub/sub that works like the communication networks used in spacecraft, allowing different components to share information without creating dependencies.
By the end of this series, you'll understand how to build applications that can scale and evolve ā whether you're developing games, web applications, or any other software system.
mindmap
root((Game Architecture))
Object Organization
Inheritance
Composition
Class Hierarchies
Behavior Mixing
Communication Patterns
Pub/Sub System
Event Emitters
Message Passing
Loose Coupling
Game Objects
Properties (x, y)
Behaviors (move, collide)
Lifecycle Management
State Management
Design Patterns
Factory Functions
Observer Pattern
Component System
Event-Driven Architecture
Scalability
Modular Design
Maintainable Code
Testing Strategies
Performance Optimization
As projects grow in complexity, code organization becomes critical. What begins as a simple script can become difficult to maintain without proper structure ā much like how the Apollo missions required careful coordination between thousands of components.
We'll explore two fundamental approaches for organizing code: inheritance and composition. Each has distinct advantages, and understanding both helps you choose the right approach for different situations. We'll demonstrate these concepts through our space game, where heroes, enemies, power-ups, and other objects must interact efficiently.
ā One of the most famous programming books ever written has to do with design patterns.
In any game, you have game objects ā the interactive elements that populate your game world. Heroes, enemies, power-ups, and visual effects are all game objects. Each exists at specific screen coordinates using x and y values, similar to plotting points on a coordinate plane.
Despite their visual differences, these objects often share fundamental behaviors:
ā Think about a game like Pac-Man. Can you identify the four object types listed above in this game?
classDiagram
class GameObject {
+x: number
+y: number
+type: string
+exists_somewhere()
}
class MovableObject {
+moveTo(x, y)
+can_move_around()
}
class TemporaryObject {
+lifespan: number
+has_lifespan()
}
class InteractiveObject {
+onCollision()
+reacts_to_stuff()
}
GameObject <|-- MovableObject
GameObject <|-- TemporaryObject
GameObject <|-- InteractiveObject
MovableObject <|-- Hero
MovableObject <|-- Enemy
MovableObject <|-- Bullet
TemporaryObject <|-- PowerUp
TemporaryObject <|-- Explosion
InteractiveObject <|-- Collectible
InteractiveObject <|-- Obstacle
Now that you understand the common behaviors game objects share, let's explore how to implement these behaviors in JavaScript. You can express object behavior through methods attached to either classes or individual objects, and there are several approaches to choose from.
The Class-Based Approach
Classes and inheritance provide a structured approach to organizing game objects. Like the taxonomic classification system developed by Carl Linnaeus, you start with a base class containing common properties, then create specialized classes that inherit these fundamentals while adding specific capabilities.
ā Inheritance is an important concept to understand. Learn more on MDN's article about inheritance.
Here's how you can implement game objects using classes and inheritance:
// Step 1: Create the base GameObject class
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
Let's break this down step by step:
x, y) and what kind of thing it is// Step 2: Add movement capability through inheritance
class Movable extends GameObject {
constructor(x, y, type) {
super(x, y, type); // Call parent constructor
}
// Add the ability to move to a new position
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
In the above, we've:
super() to initialize inherited propertiesmoveTo() method that updates the object's position// Step 3: Create specific game object types
class Hero extends Movable {
constructor(x, y) {
super(x, y, 'Hero'); // Set type automatically
}
}
class Tree extends GameObject {
constructor(x, y) {
super(x, y, 'Tree'); // Trees don't need movement
}
}
// Step 4: Use your game objects
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // Hero can move!
const tree = new Tree(10, 15);
// tree.moveTo() would cause an error - trees can't move
Understanding these concepts:
ā Take a few minutes to re-envision a Pac-Man hero (Inky, Pinky or Blinky, for example) and how it would be written in JavaScript.
The Composition Approach
Composition follows a modular design philosophy, similar to how engineers design spacecraft with interchangeable components. Instead of inheriting from a parent class, you combine specific behaviors to create objects with exactly the functionality they need. This approach offers flexibility without rigid hierarchical constraints.
// Step 1: Create base behavior objects
const gameObject = {
x: 0,
y: 0,
type: ''
};
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
};
Here's what this code does:
gameObject with position and type propertiesmovable behavior object with movement functionality// Step 2: Compose objects by combining behaviors
const movableObject = { ...gameObject, ...movable };
// Step 3: Create factory functions for different object types
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
};
}
function createStatic(x, y, type) {
return {
...gameObject,
x,
y,
type
};
}
In the above, we've:
// Step 4: Create and use your composed objects
const hero = createHero(10, 10);
hero.moveTo(5, 5); // Works perfectly!
const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() is undefined - no movement behavior was composed
Key points to remember:
**Which Pattern Should You Choose?**
**Which Pattern Should You Choose?**
```mermaid
quadrantChart
title Code Organization Patterns
x-axis Simple --> Complex
y-axis Rigid --> Flexible
quadrant-1 Advanced Composition
quadrant-2 Hybrid Approaches
quadrant-3 Basic Inheritance
quadrant-4 Modern Composition
Class Inheritance: [0.3, 0.2]
Interface Implementation: [0.6, 0.4]
Mixin Patterns: [0.7, 0.7]
Pure Composition: [0.8, 0.9]
Factory Functions: [0.5, 0.8]
Prototype Chain: [0.4, 0.3]
š” Pro Tip: Both patterns have their place in modern JavaScript development. Classes work well for clearly defined hierarchies, while composition shines when you need maximum flexibility.
Here's when to use each approach:
Object Organization Understanding: Before moving to communication patterns, ensure you can:
super() keyword works in inheritanceQuick Self-Test: How would you create a Flying Enemy that can both move and fly?
class FlyingEnemy extends Movable{ ...movable, ...flyable, ...gameObject }Real-World Connection: These patterns appear everywhere:
As applications grow complex, managing communication between components becomes challenging. The publish-subscribe pattern (pub/sub) solves this problem using principles similar to radio broadcasting ā one transmitter can reach multiple receivers without knowing who's listening.
Consider what happens when a hero takes damage: the health bar updates, sound effects play, visual feedback appears. Rather than coupling the hero object directly to these systems, pub/sub allows the hero to broadcast a "damage taken" message. Any system that needs to respond can subscribe to this message type and react accordingly.
ā Pub/Sub stands for 'publish-subscribe'
flowchart TD
A[Hero Takes Damage] --> B[Publish: HERO_DAMAGED]
B --> C[Event System]
C --> D[Health Bar Subscriber]
C --> E[Sound System Subscriber]
C --> F[Visual Effects Subscriber]
C --> G[Achievement System Subscriber]
D --> H[Update Health Display]
E --> I[Play Damage Sound]
F --> J[Show Red Flash]
G --> K[Check Survival Achievements]
style A fill:#ffebee
style B fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
style I fill:#fff3e0
style J fill:#fff3e0
style K fill:#fff3e0
The pub/sub pattern keeps different parts of your application loosely coupled, meaning they can work together without being directly dependent on each other. This separation makes your code more maintainable, testable, and flexible to changes.
The key players in pub/sub:
'PLAYER_SCORED' that describe what happened (plus any extra info)Let's create a simple but powerful event system that demonstrates these concepts:
// Step 1: Create the EventEmitter class
class EventEmitter {
constructor() {
this.listeners = {}; // Store all event listeners
}
// Register a listener for a specific message type
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
// Send a message to all registered listeners
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(listener => {
listener(message, payload);
});
}
}
}
Breaking down what happens here:
on() methodemit()Alright, let's see this in action! We'll build a simple movement system that shows how clean and flexible pub/sub can be:
// Step 1: Define your message types
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};
// Step 2: Create your event system and game objects
const eventEmitter = new EventEmitter();
const hero = createHero(0, 0);
Here's what this code does:
// Step 3: Set up event listeners (subscribers)
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.moveTo(hero.x - 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
hero.moveTo(hero.x + 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
In the above, we've:
// Step 4: Connect keyboard input to events (publishers)
window.addEventListener('keydown', (event) => {
switch(event.key) {
case 'ArrowLeft':
eventEmitter.emit(Messages.HERO_MOVE_LEFT);
break;
case 'ArrowRight':
eventEmitter.emit(Messages.HERO_MOVE_RIGHT);
break;
}
});
Understanding these concepts:
sequenceDiagram
participant User
participant Keyboard
participant EventEmitter
participant Hero
participant SoundSystem
participant Camera
User->>Keyboard: Presses ArrowLeft
Keyboard->>EventEmitter: emit('HERO_MOVE_LEFT')
EventEmitter->>Hero: Move left 5 pixels
EventEmitter->>SoundSystem: Play footstep sound
EventEmitter->>Camera: Follow hero
Hero->>Hero: Update position
SoundSystem->>SoundSystem: Play audio
Camera->>Camera: Adjust viewport
š” Pro Tip: The beauty of this pattern is flexibility! You can easily add sound effects, screen shake, or particle effects by simply adding more event listeners ā no need to modify the existing keyboard or movement code.
Here's why you'll love this approach:
The pub/sub pattern maintains simplicity as applications grow in complexity. Whether managing dozens of enemies, dynamic UI updates, or sound systems, the pattern handles increased scale without architectural changes. New features integrate into the existing event system without affecting established functionality.
ā ļø Common Mistake: Don't create too many specific message types early on. Start with broad categories and refine them as your game's needs become clearer.
Best practices to follow:
Event-Driven Architecture Understanding: Verify your grasp of the complete system:
Design Challenge: How would you handle these game scenarios with pub/sub?
Professional Connection: This pattern appears in:
Use the Agent mode to complete the following challenge:
Description: Create a simple game object system using both inheritance and the pub/sub pattern. You'll implement a basic game where different objects can communicate through events without directly knowing about each other.
Prompt: Create a JavaScript game system with the following requirements: 1) Create a base GameObject class with x, y coordinates and a type property. 2) Create a Hero class that extends GameObject and can move. 3) Create an Enemy class that extends GameObject and can chase the hero. 4) Implement an EventEmitter class for the pub/sub pattern. 5) Set up event listeners so when the hero moves, nearby enemies receive a 'HERO_MOVED' event and update their position to move toward the hero. Include console.log statements to show the communication between objects.
Learn more about agent mode here.
Consider how the pub-sub pattern can enhance game architecture. Identify which components should emit events and how the system should respond. Design a game concept and map out the communication patterns between its components.
Learn more about Pub/Sub by reading about it.
setInterval to create a simple animation looptimeline
title Game Architecture Learning Progression
section Object Patterns (20 minutes)
Code Organization: Class inheritance
: Composition patterns
: Factory functions
: Behavior mixing
section Communication Systems (25 minutes)
Event Architecture: Pub/Sub implementation
: Message design
: Event emitters
: Loose coupling
section Game Object Design (30 minutes)
Entity Systems: Property management
: Behavior composition
: State handling
: Lifecycle management
section Architecture Patterns (35 minutes)
System Design: Component systems
: Observer pattern
: Command pattern
: State machines
section Advanced Concepts (45 minutes)
Scalable Architecture: Performance optimization
: Memory management
: Modular design
: Testing strategies
section Game Engine Concepts (1 week)
Professional Development: Scene graphs
: Asset management
: Rendering pipelines
: Physics integration
section Framework Mastery (2 weeks)
Modern Game Development: React game patterns
: Canvas optimization
: WebGL basics
: PWA games
section Industry Practices (1 month)
Professional Skills: Team collaboration
: Code reviews
: Game design patterns
: Performance profiling
After completing this lesson, you now have:
Real-World Applications: These patterns directly apply to:
Professional Skills Gained: You can now:
Next Level: You're ready to implement these patterns in a real game, explore advanced game development topics, or apply these architectural concepts to web applications!
š Achievement Unlocked: You've mastered fundamental software architecture patterns that power everything from simple games to complex enterprise systems!