The Command Pattern: Input, Undo and Replays
Turning an action into an object, something that can be stored, queued, reversed and replayed, is one of the quietest and most useful ideas in game architecture, and it changes how input, editors and multiplayer code are built.

Most code that responds to the player is written as a direct line from cause to effect. A key is pressed, and inside the same Update method the character jumps; a button is clicked, and the handler immediately spends the gold and places the building. This is natural, readable and entirely sufficient for a prototype. It begins to strain the moment someone asks for a feature that treats the action itself as a thing: let me rebind this key, let me undo that, let me watch the match again.
The command pattern answers those requests with a single change of perspective. Instead of performing an action at once, the code creates an object that represents the action, with a method usually called Execute that carries it out. The action has been reified, made into a concrete thing that can be stored in a variable, placed in a list, sent across a network or written to a file, and only then, perhaps much later, performed.
The pattern appears in the 1994 catalogue by Gamma, Helm, Johnson and Vlissides, and it has a long, honourable life in game development, where it is described at length in Robert Nystrom's book Game Programming Patterns, freely readable online and warmly recommended. What follows is a tour of the pattern's main uses in games, with attention to the places where its promises are easy to make and harder to keep.
An action made into an object
At its simplest, a command is an interface with one method. In C# one might declare an interface ICommand with a method Execute, and then write small classes that implement it: a JumpCommand, a FireCommand, a PlaceBuildingCommand. Each class holds whatever data the action needs, the target unit, the position on the map, the type of building, and its Execute method does the work that would otherwise have been written inline inside an input handler.
The difference appears trivial until one notices what the object now permits. A function call vanishes the instant it returns, leaving nothing behind but its consequences. A command object remains. It can be inspected to see what it will do before it runs, logged for debugging, held back until a condition is met, or discarded if the player changes their mind. The action has acquired a lifetime, and with that lifetime comes a whole family of features.
There are two common styles for what a command knows. In one, the command is bound to its actor at creation, so a MoveCommand already contains the unit it will move. In the other, the command receives the actor as a parameter to Execute, which lets one command object be applied to any unit, a player's soldier or an enemy controlled by AI. The second style is especially useful when the same vocabulary of actions should serve both human and machine.
Decoupling input from intent
The first and most immediate use is separating input from gameplay. Rather than writing if Input.GetKeyDown on the space bar, then jump, the input layer maps each button to a command object. Pressing space looks up whatever command is bound to space and executes it. The gameplay code no longer knows anything about keys, and the input code no longer knows anything about jumping, so either side can change without disturbing the other.
Rebinding controls then becomes a matter of reassigning which command sits behind which button, a change to data rather than to code. Unity's newer Input System, with its InputAction assets and action maps, already performs much of this separation, turning physical bindings into named actions such as Jump or Attack. The command pattern sits naturally one layer above it: the action fires an event, and the handler creates or invokes the corresponding command object.
The same separation makes AI easier to write honestly. If a player's soldier is driven by commands, an AI controller can drive an enemy soldier with exactly the same commands, chosen by a decision system rather than a keyboard. Both pass through one gate into the simulation, which means the AI cannot cheat by calling methods that players cannot, and bugs in movement or attack appear for both at once, where they are easier to notice and to fix.
Undo and the stack of deeds
Undo is the feature most often associated with the pattern, and for good reason. If every command implements both Execute and Undo, then reversing an action means calling Undo on the command that performed it. An editor keeps an undo stack: each executed command is pushed onto it, and pressing undo pops the most recent command and reverses it. A second stack, for redo, receives the undone commands, so they can be executed again in order.
The difficulty lies in writing Undo correctly, because reversal requires memory. A MoveCommand cannot move a unit back unless it recorded where the unit stood before it moved. A command that destroys a wall must either keep enough information to rebuild it exactly or keep the destroyed object hidden rather than truly gone. In practice each command captures the prior state it will need inside Execute, and that captured state becomes the most delicate part of the class.
Order matters as much as memory. Undo works reliably only when commands are reversed in exactly the opposite order in which they were applied, because each one assumes the world is as the previous commands left it. Undoing a placement after a later command has moved the placed object somewhere else will restore a building into a world that no longer expects it. This is why undo stacks clear the redo stack whenever a fresh command is executed after an undo.
Unity's own Editor uses a related idea. The Undo class, with calls such as Undo.RecordObject, captures an object's state before an editor script modifies it, so the change appears in the Edit menu and can be reversed. For level editors and building tools written inside a game, a hand rolled command stack offers similar behaviour at runtime, and it is one of the few places where the full Execute and Undo pairing earns its cost without complaint.
Queues and the patience of armies
Real time strategy games put the pattern to work in another way. When a player selects a group of archers and, holding shift, clicks three points on the map and then a target, the game builds a queue of commands: move here, then here, then here, then attack. Each unit holds its own list and executes the head of the list until it completes, then advances to the next. The orders are data, waiting patiently to be carried out in sequence.
For a queue to work, commands need a notion of progress, which a single Execute call cannot express. Queued commands therefore tend to grow a little richer: a method that begins the action, a method called every frame or every simulation tick that advances it, and a way to report whether it has finished, succeeded or failed. A move order finishes when the unit arrives; an attack order finishes when the target dies or slips out of range.
The queue also gives the designer a convenient place to apply rules. A command can be validated before it is accepted, rejecting an order to build where the ground is occupied. Commands can be cancelled, replaced or reordered. A village under threat at dusk, the kind of moment that defines a game like Crown & Ashes, might clear every villager's queued chores and push a single new order to retreat behind the palisade, all without any special code beyond manipulating lists.
Replays and the demand for determinism
If every change to the game world passes through commands, then recording the commands in order, each stamped with the tick on which it was issued, produces a complete account of a match. A replay system can then start a fresh game from the same initial state and feed it the recorded commands at the recorded ticks. The file is small, because it holds only player decisions rather than the positions of every object on every frame.
This works only if the simulation is deterministic. Given the same starting state and the same commands, it must produce exactly the same results, down to the last bit. Any randomness must come from a seeded generator whose seed is recorded; physics must not depend on frame rate; iteration over unordered collections must not change order from one run to the next. A single divergence early in a match grows, tick by tick, until the replay shows armies fighting battles that never happened.
Floating point arithmetic is the most notorious obstacle. Different processors, compilers and optimization settings can produce slightly different results for the same calculation, and those differences accumulate. Unity's built in physics engine is not designed to guarantee bit identical results across machines, so games that need strict determinism often run their gameplay simulation on fixed point numbers or on carefully controlled code paths, keeping engine physics for visual effects that do not affect the outcome.
The same principle underlies lockstep networking, long used in strategy games with many units. Rather than sending the state of thousands of soldiers, each machine sends only its player's commands, and every machine runs the identical simulation. The bandwidth saving is enormous, and the cost is the same discipline replays require. Commands make both possible, but neither is a gift of the pattern alone; the determinism must be built, tested and defended across the whole codebase.
Knowing where to stop
Like any pattern, commands can be applied with more enthusiasm than judgement. Wrapping every trivial call in its own class creates a fog of tiny files, each a few lines long, and makes simple behaviour harder to follow. A camera that pans when the mouse nears the screen edge rarely needs to be undoable or replayable, and turning it into a command adds ceremony without adding capability.
The useful question is whether the action needs to exist as a thing. If it must be rebound, undone, queued, validated, sent over a network or recorded, a command earns its place. If it simply happens and is forgotten, a direct call or a C# delegate is usually enough, and in modern C# a lambda captured in an Action can serve as a lightweight command when only Execute is required and no Undo will ever be asked of it.
What the pattern teaches, even where it is not used, is a habit of seeing actions as data. Once a developer has watched a replay reconstruct a whole match from a few kilobytes of recorded orders, or pressed undo in a tool and seen a level restored stroke by stroke, the plain function call never looks quite the same again. It begins to look like a deed done in the dark, without a witness, and impossible to take back.


