The Command Pattern is a behavioural design pattern that solves the problem of tight
coupling when you need to separate the logic that’s responsible for invoking some action
from the logic that’s responsible for knowing how that action should be performed.
Some of the more popular use cases for the pattern are the implementation of
an undo feature and the ability to support macros so you can perform multiple actions at once.
In this video, we’re gonna step through a simple implementation of the undo
use case so you can see how the Command Pattern can be implemented in Unity
and get a better understanding of what the pattern is all about.
But first if you want more content just like this, sign up for my Level 2 Game Dev newsletter.
Level 2 is all about helping you develop the skills needed to take your game dev
hobby or career to the next level. Once a month we’ll send you a curated
list of content that’ll help take you to the next step on your game dev journey.
If you’re interested, visit the link in the description to sign up now.
Alright let’s take a look at our Command Pattern example project.
Our example project represents the early stages of a small turn-based tactics game.
I’m not yet sure what direction I wanna take it in so
for now I’ve only implemented a few basic actions.
Each unit can move in one of four directions and jump or spin in response to user input which I’ve
wired up using Unity’s Input System package. Like I said, early stages.
I’ve also included support for turn-based gameplay with a player and a single NPC.
Now let’s take a high-level look at the logic that’s responsible for what we’ve seen so far.
The code boils down to two MonoBehaviours, Unit and TurnController.
The Unit class exposes three public functions,
one for each of the actions that we just saw during our demo.
It also contains a bunch of plumbing that exists to facilitate the animations and effects that
correspond with each of the unit's actions. The TurnController is what drives
our turn-based gameplay. This one also has a bit of plumbing,
but the important logic can be found in the Update method where the NPC’s turn is handled
and in these three callbacks that are invoked by Unity’s input system where the
player’s input is translated directly into the actions that are called on the player’s unit.
I’m pretty happy with the logic that I’ve written so far and after reviewing the code
I don’t think it needs to be refactored so there’s only one thing left to do.
It’s time to add some more features. Now there are a couple of ways I could proceed.
I could begin fleshing out the health system, start laying down the groundwork
for keeping score, or even improve my Unit class by adding support for more actions.
But if you were paying attention earlier you may have noticed that my
rudimentary UI hints at what I’d like to work on next.
I’d like to give the player the ability to rewind or undo actions.
But looking at the logic that’s responsible for invoking actions on our unit,
it isn’t immediately obvious to me how I can even begin to support these features.
So we’re gonna have to think logically and break this down into small steps.
Let’s start by considering our undo feature. The ability relies on this concept of
rewinding which itself implies that something has been recorded.
You can’t rewind something that hasn’t been recorded somewhere.
So let’s create a class that’ll be responsible for recording our actions.
I’ll go ahead and call it “ActionRecorder”. And add a couple of methods called “Record” and
“Rewind” so we have somewhere to put our future logic.
We should also add a serialized reference of ActionRecorder to
TurnController since it’ll need to reference it later.
There. Now “Rewind” will just undo a single action
so I don’t think it’ll need any parameters. But for “Record” we’re gonna need to be
able to pass in some representation of an action so the action recorder can record it.
For example if we wanted to record a jump, we’d need an object to represent that.
Let’s call it “JumpAction” and add a method called “Execute”.
Of course, it’ll need to know which unit is performing the jump so we’ll need to include
that information somehow, as well. We could technically pass the Unit
as an argument to Execute. But that would defeat the
purpose because this object is supposed to represent a single action in its entirety.
It’s an immutable record of something that happened.
So instead let’s include the Unit as a private field that we’ll initialize in the constructor.
Now we can call “Jump” on that Unit when the “Execute” method is invoked.
And include JumpAction as an argument to the Record method
so we can call “Execute” when it’s invoked.
Finally we can pass it in when the player triggers the OnJump input callback to complete the chain.
So that’s a decent start. We’ve created an object to represent a unit
jumping which our action recorder will be able to store and undo if the player decides to rewind.
But before we implement that piece let’s create objects to represent the
other actions that units can perform. We’re gonna want ActionRecorder to be
able to record any type of action that a unit can perform but the “Record” method
is currently only accepting jump actions. To remedy that let’s extract a superclass
from the JumpAction class so the recorder can work with an abstraction that represents
a generic action instead. I’m gonna use a shortcut
here but you can easily do this by hand. We’ll call it “ActionBase”, include the
Unit field and constructor, and make Execute an abstract method.
Then use ActionBase to implement both SpinAction and MoveAction.
The only difference is that the move action will need to include a Direction
that we can pass in on the constructor.
Great. Now let’s move these into their own class files,
modify ActionRecorder’s Record method to take an ActionBase object,
and update the TurnController to use our new action classes.
All of the pieces are in place. We’ve successfully separated the
logic that’s responsible for invoking our unit’s actions
from the logic that’s responsible for knowing how those actions should be performed.
This decoupling will give us enough space to add new logic
without muddying the code that’s concerned with facilitating our turn-based gameplay.
Namely our undo logic. Let’s add that now.
First we’ll add a Stack object to ActionRecorder to maintain a first-in-last-out list of actions.
Then we’ll push each action that gets recorded onto the top of the stack.
Finally when Rewind is called
we’ll pop the last action that was pushed and call “Undo” on that action.
Of course we haven’t implemented that yet so let’s add “Undo” to ActionBase as an abstract method
and then implement it on each subclass. For Jump and Spin we’ll go ahead and
just call those again, although we could certainly implement some custom logic here.
And for Move we’ll just pass in the opposite direction.
Perfect. Let’s test it out in Unity.
Works like a charm! If you haven’t guessed
yet we’ve just used the Command Pattern to implement this feature.
Now that you’ve seen it in action, let’s review the formal definition.
The Command Pattern is a behavioral design pattern that turns a request into a stand-alone object
that contains all information about the request. This transformation lets you pass requests
as a method arguments, delay or queue a request’s execution, and support undoable operations.
The pattern consists of four key elements: command, receiver, invoker, and client.
The command is an object that knows about the receiver and invokes a method on the receiver
using values that are stored on itself. The receiver is the object that does
the work when the execute method is called on the command object.
In our case ActionBase is the command and the Unit is the receiver.
The invoker is the object that knows how to execute a command and can optionally
do bookkeeping about the command execution. In our case ActionRecorder is the invoker and
it book keeps by maintaining a stack of previously executed actions.
Finally, the client is the object that decides which receiver it assigns to the command objects
and which commands it assigns to the invoker. And we can see here that our client is the
TurnController. It should go without saying that
design patterns provide a description of common solutions to common problems.
They aren’t prescriptive about the implementation details so you
could implement our own version of the command pattern and there’d be nothing wrong with that.
So long as it fits the criteria that’s laid out by the pattern and, more importantly,
it solves whatever problem you’re trying to solve, you’re using the pattern effectively.
In the case of our example, I made a number of subtle design decisions to facilitate the
undo feature that I aimed to implement and I’m happy with the overall result.
If you’d like to take a closer look at this code yourself, I’ve made the Unity project
available to my tier two Patrons who can download it using the link in the description.
I’d like to challenge you to see if you can implement a macro command that can execute
multiple unit actions in a single turn. And don’t forget to support undo, as well.
If you can do it, send your code to my email address at charles@infalliblecode.com
or via Facebook, Twitter, or Discord. Don’t forget to sign up for the Level 2 Game Dev
newsletter for more content just like this. If you liked this video, give it a thumbs up,
leave a comment and subscribe to the Infallible Code YouTube channel.
Thanks for watching and as always I’ll catch you in the next video.