Back to Blog

Episode 6: World State Management & Events - Building an Event-Driven Game Engine

CodingButter
November 10, 2025

Dive into event-driven architecture as we implement a comprehensive event system for our game engine. Learn how to manage world state, track entity lifecycle events, and build reactive programming patterns that scale.

Episode 6: World State Management & Events

In this episode of the Built From Scratch: 2D Game Engine series, we take our Entity Component System to the next level by implementing a powerful event-driven architecture for managing world state and entity lifecycle events.

What We Built

🎯 Event System with On/Emit Pattern

We implemented a flexible event system that allows different parts of our engine to communicate without tight coupling. The system uses a simple but powerful on/emit pattern:

// Subscribe to events
world.on('entityCreated', ({ entityId }) => {
  console.log(`Entity ${entityId} was created!`);
});

// Events are automatically emitted by the World
const entity = world.createEntity(); // Triggers 'entityCreated' event

This pattern provides several benefits:

  • Loose coupling: Components don't need to know about each other
  • Reactive programming: UI can automatically update when the world changes
  • Easy debugging: Track all world changes in one place
  • Plugin system foundation: External code can hook into engine events

🔄 Entity Lifecycle Tracking

The event system tracks key moments in an entity's lifecycle:

  • entityCreated: Fired when a new entity is added to the world
  • componentAdded: Fired when a component is attached to an entity
  • worldCleared: Fired when all entities are removed
  • worldReset: Fired when the world state is completely replaced

This gives us fine-grained control over how the editor UI responds to changes in the game world.

💾 World State Serialization

We added the ability to save and load the entire world state:

// Get the current state
const state = world.getState();

// Save it somewhere (localStorage, file, database)
localStorage.setItem('savedGame', JSON.stringify(state));

// Load it back later
const savedState = JSON.parse(localStorage.getItem('savedGame'));
world.setState(savedState);

This enables:

  • Save/Load functionality: Players can save their progress
  • Level editing: Designers can create and test levels
  • Undo/Redo: Track state changes for editor features
  • Multiplayer: Share world state across network

🔍 Enhanced Query System

We improved the entity query system to be more efficient:

// Query entities that have both Position and Sprite components
for (const [entity, { Position, Sprite }] of world.query('Position', 'Sprite')) {
  // Render the sprite at the position
  renderer.drawSprite(Sprite, Position);
}

The query system now automatically sorts component types by size, starting with the smallest set to minimize iterations.

🎨 Refactored Editor Integration

The editor now uses React refs to access the world, engine, and renderer. This prevents unnecessary re-renders and provides better performance:

const worldRef = useRef<World>(null);
const engineRef = useRef<Engine>(null);

// Access the world without causing re-renders
worldRef.current.createEntity();

Key Architectural Decisions

Why an Event System?

Game engines need a way for different systems to communicate. We could have used:

  • Direct method calls: Tight coupling, hard to test
  • Global state: Difficult to track changes
  • Event system: Loose coupling, easy to extend ✅

The event system wins because it:

  • Allows the UI to react to engine changes
  • Enables a plugin architecture
  • Makes debugging easier (log all events)
  • Scales well as the engine grows

World State as a Snapshot

Instead of streaming individual changes, we serialize the entire world state as a single snapshot. This approach:

  • Simplifies save/load logic
  • Makes it easy to implement undo/redo
  • Provides a clear "source of truth"
  • Enables easy state comparison

What This Unlocks

With world state management and events in place, we can now build:

  1. Persistent Editor State: Your project saves automatically
  2. Undo/Redo System: Track and revert changes
  3. Entity Selection: Editor can track and respond to selections
  4. Property Inspection: Show component data in real-time
  5. Live Updates: Changes instantly reflect in the viewport

The Journey Continues

We've now completed six episodes of building a game engine from scratch:

  • Episode 1: Working with Workspaces
  • Episode 2: Better Foundations & A Simple Renderer
  • Episode 3: Core & Engine Package
  • Episode 4: Entity Component System
  • Episode 5: Editor UI Foundation
  • Episode 6: World State Management & Events ✨

Each episode builds on the last, creating a solid foundation for game development.

Try It Yourself

The complete source code is available on GitHub:

What's Next?

In the next episode, we'll implement an Input System to handle keyboard and mouse events. This will enable:

  • Player movement and controls
  • Editor entity selection with mouse
  • Camera panning and zooming
  • Keyboard shortcuts

Learn More

Want to follow along? Check out the full series:

If you're enjoying the series, please like, share, and subscribe on YouTube! Your support helps me continue creating educational content.

Happy coding! 🚀