Command
What is it?
The Command Pattern is a behavioral design pattern that turns a request or action into a separate object instead of executing it directly. In other words, instead of calling something like player.MoveForward() directly, the “move forward” action becomes a MoveCommand object. Then, the action is triggered simply by calling Execute() on that object.
The main purpose of this approach is to separate the code that triggers an action from the code that actually performs it. For example, an InputHandler does not move the character directly. Instead, it creates the related command and sends it to a CommandInvoker. The command itself contains what should happen, who it should affect, and, if needed, how the action can be undone.
In short, the Command Pattern puts the answer to “what should be done?” inside an object. This makes commands easier to store, queue, delay, replay, log, or undo.
When is it used?
The Command Pattern is useful when actions need to be managed, not just executed. If an action needs to be stored, undone, redone, delayed, queued, or triggered later, this pattern becomes very helpful.
One of its most common use cases is undo / redo systems. In tools like level editors, drawing programs, or text editors, each user action can be stored as a command. This allows the last executed command to be taken back by calling its Undo() method.
It is also useful for key rebinding systems. For example, if a player wants to bind the “jump” action to a different key, the input system does not need to be tightly connected to the actual behavior. The input system only chooses the correct command, while the command handles the action itself.
The pattern is also commonly used in turn-based games, replay systems, macro systems, and combo systems. Commands can be placed in a queue, executed in order, saved, or replayed later in the same sequence.
Animation
Code
using System.Collections.Generic;
using UnityEngine;
namespace Patterns.Behavioral.Command
{
public class CommandInvoker : MonoBehaviour
{
private Stack<ICommand> _history = new Stack<ICommand>();
public void ExecuteCommand(ICommand command)
{
command.Execute();
_history.Push(command);
}
public void UndoLastCommand()
{
if (_history.Count == 0) return;
var last = _history.Pop();
last.Undo();
}
}
}Advantages and Disadvantages
• Advantages:
- It makes code more flexible by separating the action trigger from the action itself.
- Undo / redo, replay, macro, and input rebinding systems become easier to build.
- Each command can store its own data and be queued or executed again later.
• Disadvantages:
- It can create too many files and too much boilerplate in small projects.
- Simple one-time actions are often easier with direct method calls.
- Too many command classes can become hard to organize as the project grows.
Tips
When using the Command Pattern in Unity, the first thing to remember is not to turn everything into a command. If an action does not need undo, replay, input rebinding, macro support, or queueing, a normal method call is usually enough. The Command Pattern is most useful when there is a real need for it.
A typical Unity structure looks like this: an ICommand interface contains Execute() and, if needed, Undo() methods. Concrete commands such as MoveCommand or AttackCommand implement this interface. A CommandInvoker executes the command and, when necessary, stores it in a Stack<ICommand>. The InputHandler does not perform the action directly; it sends the correct command to the invoker.
If you are building an undo system, think about Undo() from the beginning, not only Execute(). Undoing an action can sometimes be harder than performing it. For example, moving a character is simple, but undoing that move requires storing the character's previous position. Because of this, each command should keep the state information it needs to reverse itself.
It is also important not to bypass the invoker by calling Execute() directly from random places. If a command is not added to the history stack, it cannot be undone later. In longer games or editor tools, it is also a good idea to limit the size of the history stack so it does not grow endlessly.