Encapsulating actions as objects - recording the past!
Today's Content
đ Based on Chapter 7
Implementing a Replay System with the Command Pattern
From: Game Development Patterns with Unity 2021 (2nd Edition)
By David Baron
Available: Dulaney Browne Library or major book retailers.
This chapter teaches how to record and replay player inputs!
Today's Learning Objectives
What We'll Master
đ¯ Understand the Command Pattern structure
đ¯ Encapsulate player actions as commands
đ¯ Implement a replay recording system
đ¯ Build undo/redo functionality
đ¯ Test replay with multiple scenes
Goal: Build a replay system that records bike movements
and plays them back perfectly, frame by frame.
The Problem: Direct Input Handling
â Without Command Pattern
voidUpdate()
{
// Direct input handling - not replayable!if (Input.GetKey(KeyCode.W))
{
transform.Translate(Vector3.forward);
}
if (Input.GetKey(KeyCode.A))
{
transform.Rotate(Vector3.up, -90);
}
if (Input.GetKey(KeyCode.D))
{
transform.Rotate(Vector3.up, 90);
}
}
Problems: No recording capability, can't undo, can't replay, tightly coupled to Input system
What We Need
A way to capture player actions, store them, and replay them exactly as they happened!
What is the Command Pattern?
Command Pattern
A behavioral pattern that encapsulates a request as an object,
allowing you to parameterize clients with different requests,
queue or log requests, and support undoable operations.
â Key Concept
Turn actions into objects! Instead of calling methods directly,
create command objects that can be stored, queued, and executed later.
In Simple Terms: Commands are like recording button presses on a DVR -
you can play them back, rewind, fast-forward, or delete them!
Command Pattern: Benefits & Drawbacks
â Benefits
Decoupling: Separates invoker from receiver
Undo/Redo: Store command history
Macro Commands: Combine multiple commands
Replay Systems: Record and playback
Queuing: Execute commands in sequence
â ī¸ Drawbacks
Complexity: More classes to manage
Memory: Storing command history uses RAM
Overhead: Extra layer of abstraction
Synchronization: Replay timing can be tricky
Best For: Games with replay systems, undo functionality, input recording,
or turn-based mechanics.
RECORD: Capture player input as command objects with timestamps
STORE: Add commands to a List<Command> history
PLAYBACK: Execute stored commands in sequence at original timestamps
Key Components
Invoker: Handles recording/playback state
Commands: Store action + timestamp
Receiver: BikeController executes actions
Implementation Step 1: Command Interface
namespace Chapter.Command
{
/// Base interface for all commandspublic interfaceICommand
{
// Execute the command actionvoidExecute();
}
}
Simple But Powerful
Every command implements this interface. The Execute() method contains
the actual action to perform. This uniformity allows us to store different
command types in the same collection!
Design Note: We keep the interface simple. Commands will hold
their own data (receiver reference, parameters, timestamp).
Implementation Step 2: TurnLeft Command
namespace Chapter.Command
{
public classTurnLeft : ICommand
{
// Reference to the receiver (bike controller)private BikeController _controller;
// Constructor receives the bike controllerpublicTurnLeft(BikeController controller)
{
_controller = controller;
}
// Execute the turn left actionpublic voidExecute()
{
_controller.Turn(BikeController.Direction.Left);
}
}
}
Key Pattern: Command holds reference to receiver, Execute() delegates to receiver's method.
This decouples the caller from the receiver!