How to handle input ? (properly)

Started by
5 comments, last by MilfredCubicleX 15 years, 9 months ago
Im in the process of designing a complete game this time around, and I was wondering how to handle input given all the states my game is going to be the easy approach will be to implement a big ass switch statement where switches through all the game states first and then through all the handled key inputs but I would like to know how to make this look good and be efficient at the same time given that in my game the user could be at the main screen, in the options menu, at some dialog screen or in the actual game
Advertisement
I personally would use Function Pointers to implement a callback system for each key.
------------George Gough
and then checking which state the game is? right?
It depends on exactly how you want it to work of course, but for my current game (first person shooter), I have all my inputs defined in one place, but some of them are only used during gameplay and some only in menus.

There are 4 analog axes (move, strafe, turn, and look), although they can be mapped to buttons too in which case you just get 0 or max. Then I have an enum of digital inputs for everything else. It includes game controls (shoot, weapon forward, weapon back, etc.), and menu controls (up/down/left/right/accept/back).

Depending on how many different layouts your game uses, you might want some overlap in the controls. For example, mine could use weapon forward and weapon back in menus rather than having seperate accept and back. Also, the menu up/down/left/right could be analog, if that is useful in some other situations.

Basically the goal of the system is to be simple to configure and as portable as possible. I want it to be playable using keyboard/mouse, XBox 360 controller or similar, and Pandora as soon as it's released so I can make a port :)
The engine just exposes all the input a very useful mannor, keeping track of key state changes for things like "pressed" and "held."

If you wanted all the keyboard keys that are held down.

const Keyboard &keyboard = Engine::Input::GetKeyboard();
const Keyboard::State &keysHeld = keyboard.GetHeld();

On top of that is the interaction level (must likely what you are interested in). You've already mentioned using some sort of state machine. The currently active state could just request the input of its choice and handle its values how ever it wants.

Alternatively a state machine could be used in an "InputManager" which allows different input maps to be activated. An input map would connect some input/key combo with an event function. If the map is active, and the combo is pressed, the event gets called. When a game state is entered, the correct input map is activated.
I do it a bit like the windows message pump.

Each 'layer' of the game (main game loop, game itself, UI, console, etc) registers a callback function with the input manager when it starts. When a key gets hit (checked only by the manager in the main loop) any changed key state gets put into a message and passed through the registered handlers in order - newest to oldest. The handler can do something with the keypress if it needs to and returns true or false to signal to the manager whether to carry on processing it or remove the message.

The best part about it is you only have to handle the keys that the layer you are working on is interested in. If someone presses escape the game layer itself probably doesn't care so as soon as it sees that it is not interested it will just return false. So would the other handlers until it reaches the main game loop which probably does care and might bring up a menu. If a dialog box has been popped up though, it's handler will get the escape message first, close the box and tell the input handler the message is finished with so it doesn't get passed any further. A second press of escape in that case would be the one that reaches the main loop and brings up the menu. No huge nested if/case statements or state system is required to handle the same key in different situations.

The mouse is done the same way in my system. Handy for a game where the mouse can be used on the main play area or the UI. Dragging a scrollbar for example will just handle it and remove the message, the game layer doesn't need to know or care that it has happened.

Dan

Quote:Original post by EvilNando
the easy approach will be to implement a big ass switch statement where
switches through all the game states first and then through all the handled key inputs


No, please, no switch statement. Don't do it, please. Anything but a switch...

I use the model-view-controller pattern (read this article: http://www.gamasutra.com/features/20050414/rouwe_01.shtml scroll down 'The Controller'). The controller portion of the pattern dictates how objects receive input.

It works very simple, say you have a player class. That player class can do a number of things, move and shoot for example. So you create an abstract base class called PlayerController that has an interface that exposes methods like :
class PlayerController {public:   Vector3D GetDirectionToMove( ) = 0;   bool IsFiring( ) = 0;};


Now you can derive all kinds of different controller classes - AI controller, players that are controller by scripts, etc. For instance, a local player wants to receive input from the keyboard and mouse. So you derive a PlayerControllerKeyboardAndMouse (I like verbose naming) which implements the functions like this, perhaps:
class PlayerControllerKeyboardAndMouse : public PlayerController {public:   PlayerControllerKeyboardAndMouse( Keyboard * _keyboard, Mouse * _mouse ) : keyboard( _keyboard ), mouse( _mouse ) { }   Vector3D GetDirectionToMove( ) {      if( keyboard.IsKeyDown( W_KEY ) ) {         return Vector3D( 0.0f, 0.0f, 1.0f );      } else if( keyboard.IsKeyDown( S_KEY ) ) {         return Vector3D( 0.0f, 0.0f, -1.0f );      }      ...   }   bool IsFiring( ) {      if( mouse.IsButtonDown( LBUTTON ) ) {         return true;      } else {         return false;   }private:   Keyboard * keyboard;   Mouse * mouse;};


Now you have to create a keyboard class and a mouse class. The way I like to do it is to make them an abstract interface:

class Keyboard {public:   bool IsKeyDown( int keycode ) = 0;};class Mouse {public:   bool IsButtonDown( int buttoncode ) = 0;};


Then I have a class I use for creating a window on the win32 platform.. And this window class implements the keyboard interface and the mouse interface, because it received messages from the windows message pump. (it looks like multiple inheritence, but that's because C++ doesn't separate interface inheritance and extending base classes.

class Win32Window : public Window, public Keyboard, public Mouse {...};



Now just pass a the pointer of the windows class that implements keyboard and mouse polling to the PlayerControllerKeyboardAndMouse and Walla! the Player can now receive input.

...void CreateLocalPlayer( ) {   Player * p = new Player( new PlayerControllerKeyboardAndMouse( window, window ) );...


Finally:

class Player {public:   void UpdateController( ) {      MovePlayer( controller.GetDirectionToMove( ) );      if( controller.IsFiring( ) ) {         FireWeapon( );      }   }   void UpdateOtherStuff( ) { ... }   void Render( ) { ... }};


Note that this doesn't take the GUI into account. However, the player only polls for input when it is being updated. Say the user is in game, and then decides to open the in-game menu. You would simply stop calling the UpdateController method on the player.

This topic is closed to new replies.

Advertisement