In this lesson, you will learn how to create a robust undo system by utilizing the Command Pattern to store and reverse game state changes using a stack data structure.
The Command Stack Concept
In our previous lessons, we learned how to encapsulate actions as objects using the Command Pattern. To implement an "Undo" feature, we treat these command objects as entries in a stack. When a player performs an action, we Push() the command onto the stack. When the player triggers an undo, we Pop() the most recent command and execute its inverse.
To achieve this, every command must implement an interface that provides both an Execute() method and an Undo() method.
Storing Inverse Operations
The core of this system is ensuring that each command object holds the necessary "before" and "after" data to revert the state. For example, if you are changing a material color, the command needs to store the previousColor alongside the newColor.
public interface ICommand {
void Execute();
void Undo();
}
public class ChangeColorCommand : ICommand {
private Renderer target;
private Color previousColor;
private Color newColor;
public ChangeColorCommand(Renderer target, Color newColor) {
this.target = target;
this.newColor = newColor;
this.previousColor = target.material.color;
}
public void Execute() => target.material.color = newColor;
public void Undo() => target.material.color = previousColor;
}
Managing the Stack
In your CommandManager or InputHandler, you maintain a Stack<ICommand> object. When an action occurs:
- Instantiate the command.
- Call
Execute(). Push()the command into theundoStack.
When the undo input is received:
- Check if
undoStack.Count > 0. Pop()the command.- Call
Undo()on that command.
By coupling the action with its exact opposite logic inside the command object itself, you avoid messy conditional logic in your main game loop, keeping your architecture clean and highly scalable.
Key Takeaway: By storing the state required to reverse an action within the command object itself, you can easily manage complex game history using a simple LIFO (Last-In, First-Out) stack.