Undo & Redo with the Command Pattern in Unity & C#

Take Command of Your App Flow

Micha Davis
Nerd For Tech
6 min readJul 7, 2021

--

Execute, Undo, Redo

The final piece of my Mobile Insurance Claim App prototype will be to make this thing work:

A navigation bar with a home screen, forward and back arrows, and a hamburger menu

Home is pretty simple. I just need to close all the open UI panels. Underneath it all the main menu is right there, waiting to be seen. I can make an array of UI panel game objects, and iterate through the visible panels with a for each loop, closing each one.

Done.

The trickier part are those two forward and back buttons. These buttons are on their own panel, so I don’t have to copy them onto every panel. If I want to go back and forward between commands I need to keep a variable for the previous, present, and next panels. I’d also need every button to have access to those three variables in some way. This is starting to look like a big, messy, mutable global state and we DEFINITELY don’t want that. And if I later need to add functionality that includes other kinds of commands that might need undoing then I won’t easily be able to use the same system for both.

What we need is a way to generically identify when a command has been issued, a way to track all commands regardless of their source, and a way to preserve the content of the command and replay or reverse it at will.

For that, we turn to the Command Pattern.

Atten-SHUN!

“Encapsulate a request as an object, thereby letting users parameterize clients with different requests, queue or log requests, and support undoable operations.”

-Design Patterns: Elements of Reusable Object-Oriented Software

The gist of that pretty dense quote is we’re adding a level of abstraction which allows us to treat the execution of commands as objects. So we can put them in lists, delay their execution, pass them through functions, etc… If it isn’t clear to you yet how useful this is, bear with me: it will all make sense in the following example.

Defining a Command

The first step is to define a command. We do this with an Interface. The Interface has three methods: Execute, Undo and Redo.

Now, any class that implements this interface can issue commands. Let’s put one together for concealing and revealing panels, called PanelCommand.

So, here we run into a small problem. I need my commands to know what panel to turn off. I can’t include it as a parameter of the Execute() method — the interface Execute() doesn’t accept parameters. Adding it to the interface would destroy the abstraction we’re trying to accomplish. We need all the commands to execute the same method no matter what parameters we need.

We can fix this by adding a constructor to our PanelCommand class:

Now, when a panel creates an instance of the PanelCommand they’ll have to pass in a panel game object. We store that in a global variable and now our methods have access to the panel they’re supposed to manipulate.

We can see that at work here, in the NamePanel class:

We create a new instance of PanelCommand stored in a variable with the data type ICommand. In the process, we pass the panel GameObject the button is meant to influence. (We could add another layer of abstraction here with Actions, so that the next panel could listen for a call and send its own panel information — that’d be good encapsulation, but it’s outside the scope of the Command Pattern.) Once the instance is created, we execute the Execute() method.

I can create any number of commands in this way, each with its own constructor and execute/undo/redo methods.

Now, this is exciting and all, but all we’ve done is replace our direct calls to reveal the next panel with an indirect call to reveal the next panel. What did we even do this for?

The Command Manager

If only ALL management classes were so dashingly handsome. *sigh*

So, remember that we said we’re turning these method calls into an object? One thing we can do with these objects is add them to a List. And if we have a list of method calls, then we can repeat (or repeal) those calls in exactly the same way they were first executed.

We’ll start by declaring a new List of type ICommand. We’ll also make an index variable so we know where in the list we are at any given moment.

Now we’ll add a method to add commands to the list. We’ll request an ICommand parameter to be passed along with the call:

And as you can see, we set the command index to recalculate whenever the list changes.

We also need to change the Execute() method of the PanelCommand class to call the AddCommand(ICommand command) method:

My manager is a singleton. If that’s not your bag, working around it is pretty simple.

Finally, we come to the Undo and Redo. Now that our Execute() methods are populating the list of commands we can create a method that will step through the commands forward or backward depending on what is pressed.

So, if the “back” bool is true, then we’ve pressed the back button. We call Undo on the currently indexed instance of the command, and then scoot the command index back one. Then we make sure the index hasn’t gone negative (that would provoke an exception). And we’re done. The correct command has been undone, and the index is moved to the previous stored instance on the list.

Likewise, If the “back” bool is false, then we’ve pressed the forward button. We call Redo on the currently indexed instance of the command, and then nudge the command index up by one. Then we make sure the index hasn’t gone over the total count of the list (that would provoke an exception). And we’re done. The correct command has been redone, and the index is moved to the next stored instance on the list.

Let’s watch a gif do it:

Fantastic.

That’s all for today, and that’s also the last feature for this project! Check in tomorrow when we start our next adventure— who knows what I’ll be up to?

--

--

Micha Davis
Nerd For Tech

Unity Developer / Game Developer / Artist / Problem Solver