Thursday, January 24, 2019

Design Patterns: Command Pattern Pt 1

The Gang of Four describe the command pattern as "Encapsulate a request as an object, thereby letting users parameterise clients with different request, queue or log requests, and supports undoable operations."Another way to describe it as a thingified method call, which means to wrap a method call in a function.

The command pattern is very similar to callbacks, first-class functions, function pointers and partially applied functions. The Gang of Four also describle commands as an object oriented replacement for callbacks.

An example use for the command pattern is configurable/remappable input, what this means the user can change what actions a button/bumper/trigger can do. Usually specific actions are mapped to specific input like so:

void InputHandling()
{
    if (Input.GetButtonDown(BUTTON_X))
    {
        Reload();
    }
    else if(Input.GetButtonDown(BUTTON_Y))
    {
        SwapWeapons();
    }
    else if(Input.GetButtonDown(BUTTON_B))
    {
        Melee();
    }
    else if(Input.GetButtonDown(BUTTON_A))
    {
        Jump();
    }
}

We could set up preset functions that contain different variations of the type of input and corresponding actions. However, that might not be flexible enough and so allowing the players to swap out actions is a better alternative, which is where commands come in. We can use pointers for each button and when the player wants to change that buttons input we simply point to a new command object.

Example Code

Command Class
class Command
{
public:
virtual ~Command() { };
virtual void Execute() = 0;

};

Example Command Classes
class JumpCommand : public Command
{
public:
virtual void Execute() { Jump(); }
};

class MeleeCommand : public Command
{
public:
virtual void Execute() { Melee(); }
};

class SwapWeaponsCommand : public Command
{
public:
virtual void Execute() { SwapWeapons(); }
};

Input Handler
class InputHandler
{
public:
void HandleInput();

// Methods to bind commands

private:
Command* buttonA;
Command* buttonB;
Command* buttonX;
Command* buttonY;

};

void HandleInput()
{
    if (Input.GetButtonDown(BUTTON_X))
    {
        buttonX->Execute();
    }
    else if(Input.GetButtonDown(BUTTON_Y))
    {
        buttonY->Execute();
    }
    else if(Input.GetButtonDown(BUTTON_B))
    {
        buttonB->Execute();
    }
    else if(Input.GetButtonDown(BUTTON_A))
    {
        buttonA->Execute();
    }
}

However, this assumes that whatever functions that are wrapped in the execute functions as part of the command class, can find the player to do whatever action is required. This isn't always the case and so we can modify the code so that we pass in a reference to an object and then call a specific function of that object. This means we now make these changes to the code:

class Command
{
public:
virtual ~Command() { }
virtual void Execute(Actor& actor) = 0;
};

class SwapWeaponsCommand : public Command
{
public:
virtual void Execute(Actor& actor) 

actor.SwapWeapons(); 
}
};

Command* HandleInput()
{
    if (Input.GetButtonDown(BUTTON_X))
    {
        return buttonX;
    }
    if(Input.GetButtonDown(BUTTON_Y))
    {
        return buttonY;
    }
    if(Input.GetButtonDown(BUTTON_B))
    {
        return buttonB;
    }
    if(Input.GetButtonDown(BUTTON_A))
    {
        return buttonA;
    }

    return NULL:
}

You will notice that we now return the commands, this is because we don't know the actor to execute functions from and so once we know the command, we can pass in the reference to the actor, to the command. Like so:

Command* command = HandleInput();
if (command)
{
command->execute(actor);

}

This also means we can actually control any actor within the game, as we can just swap out which actor is passed in to the execute function.

No comments:

Post a Comment