Episode 6: World State Management & Events - Building an Event-Driven Game Engine
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:
- Persistent Editor State: Your project saves automatically
- Undo/Redo System: Track and revert changes
- Entity Selection: Editor can track and respond to selections
- Property Inspection: Show component data in real-time
- 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:
- Repository: github.com/CodingButter/GameEngineSeries
- Episode 6 Branch: ep06-world-state-management-events
- Watch on YouTube: Episode 6: World State Management & Events
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:
- Channel: CodingButter on YouTube
- Playlist: Built From Scratch: 2D Game Engine
- GitHub: GameEngineSeries Repository
If you're enjoying the series, please like, share, and subscribe on YouTube! Your support helps me continue creating educational content.
Happy coding! 🚀