Command pattern

Tulenber 29 May, 2020 ⸱ Beginner ⸱ 5 min ⸱ 2019.3.14f1 ⸱

Let’s put one more pattern to our piggy bank and see how a one named “Command” can make our life easier.

Control is an integral part of the game. Eliminate control and, at best, the game will turn into a movie. Although recently, even a movie begins to provide the ability to control what is happening on the screen, for example, Black Mirror: Brandashmyg or Mosaic by Stephen Soderberg. Like almost anything in programming, the basic creation of control in the game is not something complicated. You just need to insert a call to the function that checks the click on the left mouse button, and you have set up the shot. Then call up another function and, by pressing the space bar on the keyboard, your character begins jumping. Spread a dozen more calls all over the code, and we can go to production. Add control settings to the game? It is now possible to use Xbox/DualShock controllers in iOs, and it would be nice to add their support? Do you need to port the game to the console? No, we didn’t agree on that. So, it is clear that a simple task can always turn into a difficult one due to a small change in requirements, and in the case of setting up control, the “Command” pattern can help you smooth out the growth of complexity.

Theory

The official description of the template says that a command is a wrapper object over the call. This method allows you to transfer commands between objects easily, reconfigure their behavior, and create sequences from them. For a deeper understanding of the theoretical part, you can refer to this chapter from a Robert Nystrom book or this article.

Comparison with strategies

In their essence and basic implementation, commands are very similar to the strategies that we examined in the previous article of the cycle about templates, but their usage varies in scope. The command turns various actions into objects; as a result, you can freely interact with them; for example, for the cancellation of an operation. A strategy usually describes a change in the internal behavior of a particular entity.

Command sequences

Command objects make it easy to create sequences from them, which can be used to undo/redo actions by steps, all you need to do is to supplement the command object with data about reverse actions. It is not very applicable to action games; however, it can come in handy in a turn-based strategy or creating an editor to configure some game elements.

Practice

As a practical example, we will use the showcase from the previous article about strategies and, with the command pattern, add the control to the magician. Let’s create three commands: use a spell, switch between spells, and spawn an enemy.

Implementation

  1. Base class of the command:
1
2
3
4
5
// Base class of the command
public abstract class Command
{
    public abstract void Execute();
}
  1. Commands:
  1. Spell usage:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Spell usage command
public class FireSpellCommand : Command
{
    private Mage _mage = null;

    public FireSpellCommand(Mage mage)
    {
        _mage = mage;
    }
    
    public override void Execute()
    {
        _mage.FireSpell();
    }
}
  1. Spell switching:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Spell switching command
public class ChangeSpellCommand : Command
{
    private Mage _mage = null;

    public ChangeSpellCommand(Mage mage)
    {
        _mage = mage;
    }
    
    public override void Execute()
    {
        _mage.ChangeSpell();
    }
}
  1. Enemy spawn:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Enemy spawn command
public class SpawnEnemyCommand : Command
{
    private EnemyHandler _enemyHandler;

    public SpawnEnemyCommand(EnemyHandler enemyHandler)
    {
        _enemyHandler = enemyHandler;
    }
    
    public override void Execute()
    {
        _enemyHandler.SpawnEnemy();
    }
}
  1. Command management:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using UnityEngine;

public class InputHandler : MonoBehaviour
{
    // Objects that control the mage and the enemy
    public Mage mage = null;
    public EnemyHandler enemyHandler = null;

    // Linking actions to commands
    private Command _leftMouseClick = null;
    private Command _rightMouseClick = null;
    private Command _spaceKeyboardPush = null;

    // Start is called before the first frame update
    void Start()
    {
        // Command creation
        _leftMouseClick = new FireSpellCommand(mage);
        _rightMouseClick = new SpawnEnemyCommand(enemyHandler);
        _spaceKeyboardPush = new ChangeSpellCommand(mage);
        
    }

    // Update is called once per frame
    void Update()
    {
        // Click on the left mouse button
        if (Input.GetMouseButtonDown(0))
        {
            _leftMouseClick.Execute();
        }

        // Click on the right mouse button
        if (Input.GetMouseButtonDown(1))
        {
            _rightMouseClick.Execute();
        }

        // Space button push
        if (Input.GetKeyDown(KeyCode.Space))
        {
            _spaceKeyboardPush.Execute();
        }
    }
}

Result

It is easy to see that actions abstracted from the calling code. That simplifies the creation of settings or the extension of control methods.

Conclusion

We live in a time when mobile devices, in addition to the standard and often inconvenient control via the touchscreen, receive support for accessible game (and not only) controllers, and the game’s release rarely happens only on one platform. Also, control settings have always been an element of player friendliness. To facilitate the fight against these challenges, the command pattern that we have examined is quite suitable. However, don’t forget that overuse of patterns can turn your code into lasagna. See you next time! =)



Privacy policyCookie policyTerms of service
Tulenber 2020