How to Program in Unity: Command Pattern Explained

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
If you’ve ever played a game like Fire Emblem,  Pokemon, or pretty much anything turn-based,   there’s a great chance that you have  experienced the power of the Command Pattern.   But! The command pattern is not limited to just  turn-based games! So today we’ll dive into the   Command Pattern, learn what it is and how it  can help us make our game’s logic preservable,   interchangeable and even undoable! Welcome to Programming for Production,   a series on iHeartGameDev where we break down  relatively complex software development topics   so we understand how and when to use them  while developing our games! A quick but huge   thank you to the channel patrons for selecting  this topic! And now let’s get started!   Controller remapping, undo and redo  systems, turn based battle systems,   replay systems are just some of  the amazing features we can build   using the Command Pattern! Today,  let’s learn the fundamentals.   In modern programming we define classes, and  classes have these internal functions called   methods. Methods perform specific logic  that we also define. To use these methods,   we call, or invoke, them in other parts of  the class or other parts of the codebase.   Sometimes these methods require data called  parameters that are used in the method’s logic   and sometimes they don’t. And… none of  this is likely news to you if you’re   researching the command pattern. But, today we want to take a look at a   specific aspect of methods: execution.  The execution of a method’s logic.   When a program is running, it typically cycles  through the codebase scanning line after line of   code. Inevitably, it will reach an invoked  method! When faced with an invoked method,   the program essentially reads through  it: executing the defined logic.   And… then the program moves on. But here is a question: what if we did not   want the logic to be executed immediately? What  if we wanted to somehow tell the program that we   have this method and the data for its parameters  but we don’t want the logic to be executed yet!   How can we delay logic from being executed  for an unknown amount of time?   Enter today’s powerful solution:  The Command Pattern.   As previously stated, when programs reach invoked   methods: that method’s logic  is to be executed immediately.   This is the fundamental idea behind the command  pattern: Encapsulating a method and any data   that it may require inside a storable object so  that it can be executed at a later time.   What exactly is a “storable object” though?  In object-oriented programming and C#,   the objects we are most familiar  with are instances of classes.   Classes being the blueprints for an object,  essentially define what the object is. In other   words: what it needs to be made, details about it  and what logic it can do: its methods. Instances   of classes are when we use that blueprint while  the program is running to create the “object”.   These instances can then be stored and use  their defined methods at any time.   And that is the key difference we get by using the  Command Pattern! We replace invoked methods that   execute logic instantaneously… with commands!  Commands are classes that wrap the method   and logic we want performed. And as we just  learned, we can create an instance of a class,   allowing us to execute its methods  and logic at a later time.   So that is the idea behind the command pattern:  storing logic in an object so that it can   be used at a later time while the program  is running. But its implementation in C#,   Unity and our games are where the benefits  can really shine. Let’s map this out.   There are five components that we  want to understand with this pattern:   the abstract command, the concrete  command, the command invoker,   the command receiver, and the command client.   Without the command pattern, the client  triggers a method of a particular target:   the receiver. Typically the logic  is actually defined by the receiver   so it is something that the receiver knows how  to do: a class and a method of the class.   But with the command pattern  we add in a middle layer.   Instead of invoking a method, the client will  create an instance of a concrete command…   the invoker will store the instance…   and then later, the invoker will execute  the command on or using the receiver.   And the invoker has the guarantee that  every command can be executed because   all commands inherit the ability to be  executed from the abstract class.   So to summarize, the client creates  a command intended for the receiver,   but gives it to the invoker because the invoker  can execute the command at a later time.   This is a lot of “abstract” thinking”  so here is a… somewhat “meta”   example of the command pattern to hopefully help  conceptualize each component: if I tell you,   or “command you”, to like this video: I am the  client, the concrete command is “like this video”,   the invoker is your brain, and the receiver  is your hand. I, the client, created the   “like this video” concrete command  that was intended for the receiver:   your hand. This is because your hand knows how  to move the cursor and press the like button.   But! I gave the command to your brain  so that it stores the command instead.   Your hand might be busy: holding a phone,  drinking coffee, playing guitar. By giving   it to your brain, the command can  now be executed either immediately   or sometime late. And that’s why the  invoker is so helpful. In this case,   your brain, the invoker, can tell your hand, the  receiver” to move and press the like button.   Cool! Let’s dive into a simple project and  some code for a more concrete example.   In this project we have a lightbulb gameobject!   The lightbulb has a script  appropriately titled, “lightbulb”   and a method: “TurnOn”. “TurnOn” has the logic  necessary to, you guessed it, turn on the light!   We also have a “UserInput” script which  is a Monobehavior class that is constantly   listening for the user’s input. It  is currently hard-coded that pressing   “spacebar” will invoke the light’s  “TurnOn” method in the scene!   Let’s think of the list of command pattern  components: this example currently has the client   (the userInput file) and the  receiver (the lightbulb).   Let’s refactor this hard-coded logic to  use the command pattern instead!   What are we currently missing? The  abstract Command, the Concrete Command   and the Command Invoker. How do we  go about implementing each?   All concrete commands should  have an Execute method.   This guarantees that when the invoker is ready  to use the logic of its stored commands, all   the invoker needs to do is call Execute on any of  them. But in programming how can we be sure that   all Command classes have the Execute method? This is where the abstract command comes in.   Two ideas are to create an abstract  class or create an interface.   With an abstract class, we define  methods on a base class that others   can derive from. We would write an abstract  Command class, define the Execute method   and have our concrete Command classes derive  from this base class. However, this means   that the Execute method would always have the  same logic, severely limiting its reusability.   Alternatively, we can also make the Execute method  abstract as well. This means that any classes that   derive from the base class can and must define the  Execute method’s functionality. This is better,   but in C# we have the option to use interfaces,  and that’s the suggestion from this tutorial:   A C# interface defines a contract. This  contract is between the interface itself   and whatever class is using it: stating that  the interface’s methods must be defined.   So if the Command interface  exposes an Execute method   and all Command classes implement the Command  interface: then the classes must define Execute.   This sounds awfully like the abstract command  class with the abstract execute method.   However, a class can derive  from multiple interfaces,   which really increases the interchangeability  and reusability of Concrete Commands.   So, we’ll create a new CSharp script titled  ICommand: I being the prefix for interfaces.   We’ll define a Command interface which includes  an Execute member. Worth noting that we can call   this whatever makes sense within the context of  our games: IAction, IOrder, ICommand, etc…   Now that we’ve defined the abstract Command,  we’ll move on to the concrete Command.   As we now know, all concrete commands are going  to derive from at least the ICommand interface.   This guarantees that the Execute method is going  to be on all concrete commands. Plain and simple:   If it doesn’t derive from this  interface, it’s not a command.   To name Commands, we will start with  the name of the action or method that   the command is performing and add “Command”.  In our example project here, the command is   going to turn on the light, so we can call this  TurnOnCommand and have it derive from ICommand!   If we think of our meta example from before,  we would consider a “LikeVideoCommand”.   Now… what makes up a concrete  Command? Because of the interface,   we can guarantee that the concrete  command will define an Execute method.   It’s here where we write the logic from  the original implementation. However,   sometimes this logic will need to reference a  particular class instance or take in parameters.   So what we can do is store this  data when we create the instance   using what’s known as a constructor.  A constructor is a special method   always called when we first create the  instance of, or instantiate, a class.   In the case of our TurnOnCommand,  we’ll pass the reference to the   lightbulb on instantiation and store  it. And then in the Execute method,   we’ll have the lightbulb reference  invoke its “Turn On” method!   Ok! Let’s put this new concrete command to use!   Back in the “UserInput” file, we’ll replace  the current logic that is hardcoded.   Instead, we will instantiate a new TurnOnCommand  that can be stored in a variable of type ICommand.   And below that, we’ll invoke the  TurnOnCommand’s Execute method.   In play mode, we can now see that our lightbulb  will still turn on! Which is great… But! We   are still missing a crucial component of  the command pattern: the command invoker.   Currently there isn’t much of a difference between  the original and current implementations. BUT   by completing the command pattern  and adding in the Command invoker,   this pattern enables us to  do quite a few things:   With the invoker we can create a list of all  commands that have been and are to be executed.   By adding each command to the list, we create a  history. What makes this special is that: what   can be added, can also be taken away! Meaning, we  can relatively easily undo previous commands.   The invoker also handles the actual execution of  the commands. So we can have multiple clients or   triggers using the same invoker. To bring back  the meta example: if I were to tell you to like   the video, and your mom tells you to take out  the trash, that’s two clients creating separate   commands for the same invoker. And the invoker can  even decide how it wants to execute each command   in the list. It can execute them all at once, it  can execute the commands that are most recently   added, execute the oldest, the most important…  That’s the cool thing about the invoker,   is that we can tailor its functionality  to the needs of our game’s systems.   This leads to a noteworthy point about the Command  Pattern: its actual implementation can vary.   More specifically, while the interface and  concrete commands are typically structured   how we’ve used them here, the logic within the  command invoker is what can take on many forms.   So while this lightbulb is a rather  simple example, in the future,   we’ll take a look at alternative implementations  that conform to the game we’re actually making.   Today’s video is more about understanding  the concepts behind the pattern.   For this first example of what an invoker  can be, let’s create a new class called   “LightSwitch”. This lightswitch class  will be in charge of turning on our light.   To do so, it will need to somehow access the  TurnOnCommand that is currently in the client,   and call Execute on that Command. The most basic  way we can do this is to store a single Command   within the invoker, called OnCommand. We would  set this in the constructor of the invoker.   And in a local method, called  PowerOn, execute the OnCommand.   In our UserInput file, we can now create and  store the invoker, instantiating a LightSwitch.   When we instantiate the LightSwitch,  it expects the TurnOnCommand.   And now when we press spacebar, we’ll  use the LightSwitch’s PowerOn method!   Testing this, we’ll see the  light once again turns on!   Awesome! At this point, we have successfully  decoupled the client and the receiver as much   as we possibly can using the command pattern.  What this means is that the client knows as   little as possible about the receiver, which  ultimately makes them both more independent.   Ok! We now realize that a lightbulb  and lightswitch should also be able   to turn off. We can add the “turn off”  functionality to the light in multiple ways,   but the way we will do it today is by  refactoring our lightbulb class and commands.   Instead of a “TurnOn” method in the lightbulb  class, we’ll have a “TogglePower” method which   will either turn the bulb on or off  depending on its current state.   We’ll then rename and modify the TurnOnCommand  appropriately to TogglePowerCommand   and change the Execute definition to  call the lightbulb’s “TogglePower”.   We’ll also rename the PowerOn method  of the lightswitch to TogglePower   as well! In the UserInput class, we’ll rename  the TurnOnCommand to TogglePowerCommand,   and change the LightSwitch’s “PowerOn”  method to “TogglePower” as well.   Testing again in play mode and now when we press  spacebar, the lightbulb will turn on and off!   Now this is one of the simplest examples of  the invoker, but we can make the invoker do   more. This implementation is missing what most  invokers include: a stored list of commands.   And without the list, we’re not maximizing  the Command Pattern’s usefulness.   To really showcase this, let’s extend our  example a bit. We’ve gone ahead and upgraded   our lightbulb. It is now one of those fancy  Philips hue lights that can change color.   This functionality is defined within two  new methods on the lightbulb class that   will change the bulb’s color to either the  one that is passed in or a random color.   And we also have a new command:  ChangeColorCommand. This command will also   derive from ICommand and we’ll set the randomcolor  method of the lightbulb in Execute.   This increase in functionality requires  something more advanced than a lightswitch.   So let’s remove the code related to this first  invoker in our UserInput file, and then set up   our more advanced invoker. Let’s imagine that  Philip hue lightbulbs are controlled with an app.   We’ll create a new invoker  class called: LightApp.   Where the LightSwitch stored a single command at  instantiation, the LightApp will instead store a   list of Commands over time! For this tutorial,  we will use a “Stack” as the type of list   but, as mentioned, there are multiple other  list types that the invoker can use. Of course,   this list starts out empty, so how do we actually  add commands like the TogglePowerCommand?   Instead of a TogglePower method like the  LightSwitch, we’ll rename that to something   more generic: AddCommand. AddCommand  will expect a Command as a parameter,   and inside of this method, it will  invoke Execute on the Command argument,   followed by adding the command to the  list using the stack’s push method!   In the Client, or UserInput file,  we can add the LightApp invoker.   We can then use the LightApp’s AddCommand  method when SpaceBar is pressed,   passing in a new TogglePowerCommand. And we can do  the same for the ChangeColorCommand on a separate   input listener: we’ll use the “C” key. Now, pressing SpaceBar will turn on the lightbulb,   add the command to the list and execute  it. Pressing “C” will change the color,   add another command to the list, and  execute that command. And one more time,   pressing space will turn off the light,  adding a third command and executing it.   Wonderful! We can now turn the light on, off and  change its color! But we might notice that this   list doesn’t do much for us… yet! The last  thing we’ll cover in this tutorial is one of   the many benefits that the Command Pattern  offers which is to undo previous commands.   As we know, the Command interface currently  guarantees that all of the Commands will define   an Execute method. We can use this same guarantee  to ensure that all Commands have an Undo method   as well! So, updating the ICommand  interface, we’ll add in Undo.   Our two commands must now also define the  Undo method. But how does Undo work? When   we use each command, we are essentially  telling the Light to do a specific thing,   so if we wanted to “Undo”, we  would have it do the opposite!   For the TogglePowerCommand, this would be rather  hard to tell the difference between triggering   undo and just pressing space again because  both will result in the light turning off or on   depending on its current state. But changing  the light color is a different story.   When creating an instance  of the ChangeColorCommand,   we will store the current color in a  separate variable using the constructor.   This means that when the command is  initially added to the list and is executed,   changing the color of the lightbulb, that command  will remember and store the previous color!   So to define the undo functionality  of the ChangeColorCommand,   all we need is to set the lightbulb instance’s  color back to the stored previous color!   And for the TogglePowerCommand, the  undo will just reverse the current state   using the lightbulbs toggle power.   However, we don’t have a place in our code  that calls a Command’s undo method. As we know,   the invoker is the middleman that actually  calls the methods of the commands.   The AddCommand calls Execute on each of the  Commands and adds it to the list. Let’s create   an UndoCommand method that calls Undo on the  last element and then removes it from the list!   We can easily remove the last  command using the Stack’s Pop method.   All we need to do is add another input  handler for the Client, we’ll use the “Z” key,   and invoke the LightApp’s UndoCommand method. If we enter play mode, we can turn the light on,   change the colors and press Z to undo  our previous commands! Fantastic!   The Command Pattern is pretty wild because it can  be defined in so many ways, but hopefully now you   can see it can lead to results like the mechanics  we mentioned at the start. In future videos   we’ll definitely expand more on this  pattern with advanced implementations   to showcase what it can help accomplish.   Awesome! Thank you to all of the patrons for  choosing this topic. It was a blast getting to   learn yet another complex behavioral pattern  and make such a fun project. If YOU would   like to vote on the next tutorial that we’ll  cover and access this projects files, consider   supporting the channel and join the Patreon! And  a special shoutout to one beets and zack etier   for the extra support! If you’re interested in  joining an amazing community, we’d love to have  
Info
Channel: iHeartGameDev
Views: 65,708
Rating: undefined out of 5
Keywords: How to program in unity: command pattern explained, command pattern explained, command pattern unity, game development patterns, the command pattern, how to use the command pattern, what is the command pattern, programming in unity, c# command pattern, command pattern tutorial, unity programming patterns, unity programming tutorial, how to program in unity, unity design patterns, how to program in c# unity, c# design patterns, command pattern, command pattern example, 2022
Id: oLRINAn0cuw
Channel Id: undefined
Length: 22min 36sec (1356 seconds)
Published: Sun Mar 06 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.