Skip to content

Implementing a Command Design Pattern in Unity

Implementing a Command Design Pattern in Unity

In this tutorial, we will learn about Command Design Pattern and then implement a Command Design Pattern in Unity to solve the movement of a game object.

Read also: Implementing a Finite State Machine Using C# in Unity

Read Also: Solving 8 puzzle problems using A* star search

Download the 8 Puzzle Unlimited App from Google Play.


Introducing the Command Design Pattern

Requests, Orders and Commands: We are all familiar with them in real life; one person sends a request (or an order or a command) to another person to perform (or do not perform) some assigned tasks.  This thing works similarly in software development and programming: one component’s request is transmitted to another to execute specific functions in the Command pattern.

Definition: A command pattern is a behavioural design pattern in which a request is transformed into an object that encapsulates (contains) all information needed to perform an action or trigger an event later. This transformation into objects lets you parameterise methods with different requests, delay a request and/or queue the request’s execution.

Good and robust software design should be based on the principle of separation of concerns. Separation can usually be achieved by breaking up the application into multiple layers (or software components). A commonly recurring example is dividing an application into a graphical user interface that handles only the graphics part and another into a logic handler for the business logic part.

The GUI layer is responsible for rendering a beautiful picture on the screen and capturing any inputs. The logic handler could perform actual calculations for a specific task. The GUI layer thus delegates the work to the underlying layer of business logic.

Read Implementing Player Controls With Finite State Machine Using C# in Unity

Structure of a Command Design Pattern

The structure of the command design pattern is represented below by a UML class diagram. The various classes are explained in detail below.

Class diagram for Command Design Pattern

To implement a command pattern, we will need the abstract Command, concrete Commands, an Invoker, a Client and the Receiver classes. 

Command

The Command is usually designed as an interface with just one or two methods for executing and undoing the command operation. All concrete command classes must be derived from this Interface and should implement the actual Execute and, if necessary, then the Undo implementation.

public interface ICommand      
{ 
    void Execute();
    void ExecuteUndo();       
}Code language: PHP (php)

Invoker

The Invoker (also known as the Sender) class is responsible for initiating requests. This is the class that triggers the necessary command. This class must have a variable that stores the reference to a command object or a container of commands object. The invoker triggers a command instead of sending the request directly to the receiver. Note that the invoker isn’t responsible for creating the command object. Usually, it gets a pre-created command from the client via the constructor.

Client

The Client creates and configures concrete command objects. The client must pass all request parameters, including a receiver instance, into the command’s constructor. After that, we can associate the resulting command with one or multiple invokers. A client can be any class that creates different command objects.

Receiver (Optional)

The Receiver class is the class that receives the command and contains almost all of the business logic. Almost any object may act as a receiver. Most commands only handle the details of how a request is passed to the receiver, while the receiver itself does the actual work.

Concrete Commands

Concrete Commands are derived from the Command interface, and they implement various kinds of requests. A concrete command isn’t supposed to perform the work independently but rather to pass the call to one of the business logic objects or receivers (as described above). However, for the sake of simplicity, we will merge these classes.

Parameters required to execute a method on a receiving object can be declared as fields in the concrete command. You can make command objects immutable by only allowing the initialisation of these fields via the constructor.

See also: https://refactoring.guru/.

Implementing a Command Design Pattern in Unity

We will implement a Command Design pattern in Unity to solve the movement of a game object by applying different types of movements. Each of these movements will be implemented as a command. We will also implement an undo function to be able to undo the operations in reverse order.

So let’s start!

Create a new 3D Unity Project

We will start by creating a 3D Unity project. Name it as CommandDesignPattern.

Create the Ground

For our tutorial, we will create a simple Plane object which will form our ground. Right-click on the Hierarchy window and create a new Plane game object. Rename it to Ground and resize it to 20 units in x and 20 units in the z-axis. You can apply a colour or texture to the ground to make it look more visually appealing.

Create the Player

We will now create a Player game object. For our tutorial, we will use a Capsule to represent the player game object. So go ahead, right-click on the Hierarchy window and create a new Capsule game object. Rename it to Player.

Create the GameManager.cs Script

Select the Ground game object and add a new script component. Name the script GameManager.cs.

We shall now implement the movement of the Player object.

For this, we add a public GameObject variable called _player.

public GameObject mPlayer;Code language: C# (cs)

Now drag and drop the Player game object from the Hierarchy to the Player field in the inspector window.

Implement Movement for Player

We will use the Up, Down, Left and Right arrow keys for our movement of the Player.

For a start, we will implement this movement the usual way we are used to doing it. Let’s go ahead and implement the Update method.  For simplicity, we will implement the discrete movement of 1 unit for each keystroke in the correct directions.

void Update()
{
    Vector3 dir = Vector3.zero;
    if (Input.GetKeyDown(KeyCode.UpArrow))
        dir.z = 1.0f;
    else if (Input.GetKeyDown(KeyCode.DownArrow))
        dir.z = -1.0f;
    else if (Input.GetKeyDown(KeyCode.LeftArrow))
        dir.x = -1.0f;
    else if (Input.GetKeyDown(KeyCode.RightArrow))
        dir.x = 1.0f;
    if (dir != Vector3.zero)
    {
        _player.transform.position += dir;
    }
}Code language: C# (cs)

Click the Play button and try out the game. Press the Up, Down, Left and Right arrow keys to see the movement of the Player.

Implement Right Click to Moveto Location

We will now implement a right-click on the Ground for the Player to move to the clicked location. How do we do that?

First of all, we will need the position of the clicked point on the Ground.

public Vector3? GetClickPosition()
{
    if(Input.GetMouseButtonDown(1))
    {
        RaycastHit hitInfo;
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if(Physics.Raycast(ray, out hitInfo))
        {
            //Debug.Log("Tag = " + hitInfo.collider.gameObject.tag);
            return hitInfo.point;
        }
    }
    return null;
}Code language: C# (cs)

What is the return type Vector3?

Use of the ? operator for return types in C#, for example.

public int? myProperty { get; set; }

It means that the value type in question is .nullable

Nullable types are instances of the System.Nullable struct. A nullable type can represent the correct range of values for its underlying value type, plus an additional null value. For example, a Nullable<Int32>, pronounced “Nullable of Int32,” can be assigned any value from -2147483648 to 2147483647. A Nullable<bool> can be assigned the values true, false, or null. The ability to assign null to numeric and Boolean types is beneficial when dealing with databases and other data types that contain elements that may not be assigned a value. For example, a Boolean field in a database can store the values true or false or undefined.

Now that we have the clicked position, we will need to implement the MoveTo function. Our MoveTo function should move the player smoothly. We will implement this as a Coroutine with a linear interpolation of the displacement vector.

public IEnumerator MoveToInSeconds(GameObject objectToMove, Vector3 end, float seconds)
{
    float elapsedTime = 0;
    Vector3 startingPos = objectToMove.transform.position;
    end.y = startingPos.y;
    while (elapsedTime < seconds)
    {
        objectToMove.transform.position = Vector3.Lerp(startingPos, end, (elapsedTime / seconds));
        elapsedTime += Time.deltaTime;
        yield return null;
    }
    objectToMove.transform.position = end;
}Code language: C# (cs)

Now, all we need is to call the coroutine whenever the right mouse button is clicked.

Amend the Update method by adding the following lines of code.

****
    var clickPoint = GetClickPosition();
    if (clickPoint != null)
    {
        IEnumerator moveto = MoveToInSeconds(_player, clickPoint.Value, 0.5f);
        StartCoroutine(moveto);
    }
****Code language: C# (cs)

Click the Play button and try out the game. Press the Up, Down, Left, Right arrow keys and right-click on the ground to see the movement of the Player.

Implementing Undo Operation

How do we implement the Undo operation? Where is Undo of movement necessary? Ponder for 5 minutes.

Implement the Command Design Pattern in Unity

We are now going to implement the Undo method for each of the movement operations that we can do using both the keypresses and right-clicking.

The easiest way to implement the Undo operation is to use the Command Design Pattern by implementing a Command Design Pattern in Unity.

For our command pattern, we will convert all the movement types to commands. Let’s start by creating the Command Interface.

Command Interface

public interface ICommand
{
    void Execute();
    void ExecuteUndo();
}Code language: C# (cs)

Our Command interface has two methods. The first one is the normal Execute method, and the second one is the ExecuteUndo method that does the Undo operation. We will need to implement these two methods (besides other methods if necessary).

Now let’s convert our basic movement into a concrete command.

CommandMove

public class CommandMove : ICommand
{
    public CommandMove(GameObject obj, Vector3 direction)
    {
        mGameObject = obj;
        mDirection = direction;
    }
    public void Execute()
    {
        mGameObject.transform.position += mDirection;
    }
    public void ExecuteUndo()
    {
        mGameObject.transform.position -= mDirection;
    }
    GameObject mGameObject;
    Vector3 mDirection;
}Code language: C# (cs)

CommandMoveTo

public class CommandMoveTo : ICommand
{
    public CommandMoveTo(GameManager manager, Vector3 startPos, Vector3 destPos)
    {
        mGameManager = manager;
        mDestination = destPos;
        mStartPosition = startPos;
    }
    public void Execute()
    {
        mGameManager.MoveTo(mDestination);
    }
    public void ExecuteUndo()
    {
        mGameManager.MoveTo(mStartPosition);
    }
    GameManager mGameManager;
    Vector3 mDestination;
    Vector3 mStartPosition;
}Code language: C# (cs)

See how the ExecuteUndo method is implemented. It is actually just doing the reverse of what the Execute method is doing.

Invoker Class

We will now implement the Invoker class. Remember that the Invoker class is the class that is responsible for keeping the commands. Also, remember that for Undo to work, we will need to implement a Last In First Out (LIFO) kind of data structure.

What is LIFO? How can we implement a LIFO? Introducing the Stack data structure.

C# includes a particular type of collection that stores elements in LIFO style (Last In First Out). This collection contains a generic and non-generic Stack. It provides a Push() method to add value to the top (last), Pop() method to remove the top (or the last) value, and Peek() method to retrieve the top value.

We will now implement the Invoker class, which will hold a Stack of commands.

public class Invoker
{
    public Invoker()
    {
        mCommands = new Stack<ICommand>();
    }
    public void Execute(ICommand command)
    {
        if (command != null)
        {
            mCommands.Push(command);
            mCommands.Peek().Execute();
        }
    }
    public void Undo()
    {
        if(mCommands.Count > 0)
        {
            mCommands.Peek().ExecuteUndo();
            mCommands.Pop();
        }
    }
    Stack<ICommand> mCommands;
}Code language: C# (cs)

Notice how the Invoker of the Commands implements the Execute and Undo methods. When calling the Execute method, the Invoker pushes the command into the stack by calling the Push method of the Stack and then executing the command’s Execute method. The command at the top is retrieved by the Peek method of the Stack.

Undo the Invoker calls the ExecuteUndo method of the command by retrieving the top command from the Stack (using the Peek method). After that, the Invoker is releasing the top command by calling the Pop method of the Stack.

We are all set to make use of our Invoker and the commands. For this, we will first add a new variable to our GameManager class for the Invoker object.

private Invoker mInvoker;Code language: PHP (php)

Then we will instantiate the mInvoker object in the Start method of the GameManager script.

mInvoker = new Invoker();Code language: JavaScript (javascript)

Undo

We will now call the Undo by pressing the U key. Add the following code in the Update method before the closing bracket.

    // Undo 
    if (Input.GetKeyDown(KeyCode.U))
    {
        mInvoker.Undo();
    }Code language: JavaScript (javascript)

Using Commands

We will now modify the Update method to use the new Command pattern.

void Update()
{
    Vector3 dir = Vector3.zero;
    if (Input.GetKeyDown(KeyCode.UpArrow))
        dir.z = 1.0f;
    else if (Input.GetKeyDown(KeyCode.DownArrow))
        dir.z = -1.0f;
    else if (Input.GetKeyDown(KeyCode.LeftArrow))
        dir.x = -1.0f;
    else if (Input.GetKeyDown(KeyCode.RightArrow))
        dir.x = 1.0f;
    if (dir != Vector3.zero)
    {
        //Using command pattern implementation.
        ICommand move = new CommandMove(mPlayer, dir);
        mInvoker.Execute(move);
    }
    var clickPoint = GetClickPosition();
    //Using command pattern right click moveto.
    if (clickPoint != null)
    {
        CommandMoveTo moveto = new CommandMoveTo(
            this, 
            mPlayer.transform.position, 
            clickPoint.Value);
        mInvoker.Execute(moveto);
    }
    // Undo 
    if (Input.GetKeyDown(KeyCode.U))
    {
        mInvoker.Undo();
    }
}Code language: C# (cs)

Click the Play button and try out the game. Press the Up, Down, Left, Right arrow keys to see the movement of the Player and the “u” key to Undo in reverse order. 

Conclusion

The Command design pattern is one of the twenty-three well-known GoF design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, reuse and maintain.

Complete Script for Unity

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public interface ICommand
    {
        void Execute();
        void ExecuteUndo();
    }

    public class CommandMove : ICommand
    {
        public CommandMove(GameObject obj, Vector3 direction)
        {
            mGameObject = obj;
            mDirection = direction;
        }

        public void Execute()
        {
            mGameObject.transform.position += mDirection;
        }

        public void ExecuteUndo()
        {
            mGameObject.transform.position -= mDirection;
        }

        GameObject mGameObject;
        Vector3 mDirection;
    }

    public class Invoker
    {
        public Invoker()
        {
            mCommands = new Stack<ICommand>();
        }

        public void Execute(ICommand command)
        {
            if (command != null)
            {
                mCommands.Push(command);
                mCommands.Peek().Execute();
            }
        }

        public void Undo()
        {
            if (mCommands.Count > 0)
            {
                mCommands.Peek().ExecuteUndo();
                mCommands.Pop();
            }
        }

        Stack<ICommand> mCommands;
    }
    public GameObject mPlayer;
    private Invoker mInvoker;

    public class CommandMoveTo : ICommand
    {
        public CommandMoveTo(GameManager manager, Vector3 startPos, Vector3 destPos)
        {
            mGameManager = manager;
            mDestination = destPos;
            mStartPosition = startPos;
        }

        public void Execute()
        {
            mGameManager.MoveTo(mDestination);
        }

        public void ExecuteUndo()
        {
            mGameManager.MoveTo(mStartPosition);
        }

        GameManager mGameManager;
        Vector3 mDestination;
        Vector3 mStartPosition;
    }

    public IEnumerator MoveToInSeconds(GameObject objectToMove, Vector3 end, float seconds)
    {
        float elapsedTime = 0;
        Vector3 startingPos = objectToMove.transform.position;
        end.y = startingPos.y;
        while (elapsedTime < seconds)
        {
            objectToMove.transform.position = Vector3.Lerp(startingPos, end, (elapsedTime / seconds));
            elapsedTime += Time.deltaTime;
            yield return null;
        }
        objectToMove.transform.position = end;
    }

    public Vector3? GetClickPosition()
    {
        if (Input.GetMouseButtonDown(1))
        {
            RaycastHit hitInfo;
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out hitInfo))
            {
                //Debug.Log("Tag = " + hitInfo.collider.gameObject.tag);
                return hitInfo.point;
            }
        }
        return null;
    }



    // Start is called before the first frame update
    void Start()
    {
        mInvoker = new Invoker();
    }

    // Update is called once per frame
    void Update()
    {
        Vector3 dir = Vector3.zero;

        if (Input.GetKeyDown(KeyCode.UpArrow))
            dir.z = 1.0f;
        else if (Input.GetKeyDown(KeyCode.DownArrow))
            dir.z = -1.0f;
        else if (Input.GetKeyDown(KeyCode.LeftArrow))
            dir.x = -1.0f;
        else if (Input.GetKeyDown(KeyCode.RightArrow))
            dir.x = 1.0f;

        if (dir != Vector3.zero)
        {
            //----------------------------------------------------//
            //Using normal implementation.
            //mPlayer.transform.position += dir;
            //----------------------------------------------------//


            //----------------------------------------------------//
            //Using command pattern implementation.
            ICommand move = new CommandMove(mPlayer, dir);
            mInvoker.Execute(move);
            //----------------------------------------------------//
        }

        var clickPoint = GetClickPosition();

        //----------------------------------------------------//
        //Using normal implementation for right click moveto.
        //if (clickPoint != null)
        //{
        //    IEnumerator moveto = MoveToInSeconds(mPlayer, clickPoint.Value, 0.5f);
        //    StartCoroutine(moveto);
        //}
        //----------------------------------------------------//

        //----------------------------------------------------//
        //Using command pattern right click moveto.
        if (clickPoint != null)
        {
            CommandMoveTo moveto = new CommandMoveTo(
this, mPlayer.transform.position, clickPoint.Value);
            mInvoker.Execute(moveto);
        }
        //----------------------------------------------------//


        //----------------------------------------------------//
        // Undo 
        if (Input.GetKeyDown(KeyCode.U))
        {
            mInvoker.Undo();
        }
        //----------------------------------------------------//
    }

    public void MoveTo(Vector3 pt)
    {
        IEnumerator moveto = MoveToInSeconds(mPlayer, pt, 0.5f);
        StartCoroutine(moveto);
    }
}

Code language: C# (cs)

Read My Other Tutorials

  1. Implement Camera Pan and Zoom Controls in Unity2D
  2. Implement Drag and Drop Item in Unity
  3. Graph-Based Pathfinding Using C# in Unity
  4. 2D Grid-Based Pathfinding Using C# and Unity
  5. 8-Puzzle Problem Using A* in C# and Unity
  6. Create a Jigsaw Puzzle Game in Unity
  7. Implement a Generic Pathfinder in Unity using C#
  8. Create a Jigsaw Puzzle Game in Unity
  9. Generic Finite State Machine Using C#
  10. Implement Bezier Curve using C# in Unity
  11. Create a Jigsaw Tile from an Existing Image
  12. Create a Jigsaw Board from an Existing Image
  13. Solving 8 puzzle problem using A* star search
  14. A Configurable Third-Person Camera in Unity
  15. Player Controls With Finite State Machine Using C# in Unity
  16. Finite State Machine Using C# Delegates in Unity
  17. Enemy Behaviour With Finite State Machine Using C# Delegates in Unity
  18. Augmented Reality – Fire Effect using Vuforia and Unity
  19. Implementing a Finite State Machine Using C# in Unity
  20. Solving 8 puzzle problem using A* star search in C++
  21. What Are C# Delegates And How To Use Them
  22. How to Generate Mazes Using Depth-First Algorithm

References

Wikipedia Design Patterns

Wikipedia Command Design Pattern

Refactoring Guru

Game Programming Patterns

Design Patterns in Game Programming

Leave a Reply

Your email address will not be published. Required fields are marked *