Engine Design – Input Commands


I’m currently working on my own game engine and while it’s no where near from being finished, I’ve applied a couple of design patterns when creating the different systems. In the coming weeks I’ll go through a system each at a time and write about how I have implemented them and what problems I might have stumbled upon.

This week’s system is the input system which enables the player, may it be everything from a tank to a warrior with an axe, to move and perform all their desired actions.

When first starting on the development of the input system i set out two goals which needed to be met in order for it to be a successful implementation.

  • First, all actions needed to be rebindable, players should not be forced to use a specific layout. Forced mapping layouts cause trouble when users don’t have that particular region layout on their keyboard and it certainly makes it difficult for physically disabled persons to enjoy a great game.
  • Second, when checking whether an action should be called there should be a simple way of doing so. An easy to use interface which is as simple as an if statement.

The current solution works like this.

If you want to add a new command with corresponding input you type the following piece of code.

Input->setInput("Walk Left", InputObject::KEYBOARD, sf::Keyboard::A, true); 

As you probably can see, this enables the player to walk left by pressing the A key on their keyboard. The last boolean value is used to set whether the command will be able to be rebound. This choice can be good to have when creating critical commands that e.g. changes window mode. The way you can utilize the commands is through these two functions.

if ( Input->;called("Walk Left") )  
    { /* Player walks left */ }
if ( Input->calledOnce("Jump") )  
    { /* Player jumps */ }

The called() function returns true each frame update if the matching input device’s switch is activated.
The calledOnce() function is quite self explanatory, it only returns true the first frame update when the input device’s switch is activated.

The way InputObject is set up looks like this.

enum INPUT

struct SCommand
	int value[InputObject::INPUT_COUNT];
	bool rebindable[InputObject::INPUT_COUNT];

std::map m_command;
std::map m_current;
std::map m_previous;

I have removed much of the class structure and only kept the necessary for the sake of readability .
The input device is denoted through the enum INPUT. The struct SCommand holds what key or button you have to press in order for the command to activate. Hare is also the rebind state stored. Each array currently holds three values, one for each input device. That means that each command can have a input from each of the three input devices.

Then there are three std::map data structures which keep track of the input changes which the engine forwards. The m_command keeps track of all the stored bindings while the m_current and m_previous takes care of the current state of each command.

The above function called() returns true if the m_current value is true for the corresponding command. While the calledOnce() function need to have the m_current set to false and m_previous to true in order to return true.

Currently the support for multiple players is somewhat lacking and in future versions I’d like to enable commands to be bound to a player index. If a solution were to be implemented then you would be able to rebind buttons on controllers and each players controller would get updated bindings. As of now the only solution is to keep track of a global current_player index when looping through each players update function or have a separate command for each player like so: “Walk Left 1” “Walk Left 2”

That is all I have to share for now but if you’d like to learn more about the command pattern then I’d strongly recommend reading Game Programming Patterns: Command!

~Per “Gimmic” Johansson


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s