• Advertisement
Sign in to follow this  

Input Manager

This topic is 3022 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

So building an input manager and always done pretty "hacky" solutions before. The main question regarding this input manager is really how to handle function calls when a certain key is pressed. It doesn't feel so smooth to have all the different function calls inside the input manager, it requires the input manager instance to own every object that uses input to do the appropriate function calls wich can get messy. Thinking of several solutions but I feel quite lost, how have you people dealt with "input managers"? *EDIT* Just noted I was still in the DirectX/XNA forum while posting this, was thinking of posting it in Game Programming but not much to be done now so gimme your ideas here if you have some :) [Edited by - rostigcykel on November 3, 2009 9:31:12 PM]

Share this post


Link to post
Share on other sites
Advertisement
Moving to game programming...

I'd create an interface to handle input, and pass classes derived from that as input handlers. An example:

class IInputNotifier
{
public:
IInputNotifier() {}
virtual ~IInputNotifier() {}

virtual void OnKeyDown(WPARAM wParam, LPARAM lParam) = 0;
virtual void OnKeyUp(WPARAM wParam, LPARAM lParam) = 0;
};

class InputManager
{
public:
// Usual methods

void AddListener(int nKey, IInputNotifier* pListener);

// Called from your window proc:
void OnKeyDown(WPARAM wParam, LPARAM lParam);
void OnKeyUp(WPARAM wParam, LPARAM lParam);

private:
typedef std::vector<IInputNotifier*> ListenerVec;
typedef ListenerVec::iterator ListenerVecIt;

ListenerVec m_listeners[256];
};

void InputManager::AddListener(int nKey, IInputNotifier* pListener)
{
m_listeners[nKey].push_back(pListener);
}

void InputManager::OnKeyDown(WPARAM wParam, LPARAM lParam)
{
for(ListenerVecIt it=m_listeners[wParam].begin(); it!=m_listeners[wParam].end(); ++it)
(*it)->OnKeyDown(wParam, lParam);
}

void InputManager::OnKeyUp(WPARAM wParam, LPARAM lParam)
{
for(ListenerVecIt it=m_listeners[wParam].begin(); it!=m_listeners[wParam].end(); ++it)
(*it)->OnKeyUp(wParam, lParam);
}



And an example usage:

class Foo : public IInputNotifier
{
public:
Foo() {}
virtual ~Foo() {}

virtual void OnKeyDown(WPARAM wParam, LPARAM lParam)
{
std::cout << "Key down! wParam=" << wParam << ", lParam=" << lParam << std::endl;
}

virtual void OnKeyUp(WPARAM wParam, LPARAM lParam)
{
std::cout << "Key up! wParam=" << wParam << ", lParam=" << lParam << std::endl;
}
};


Error checking and suchlike omitted for brevity, and you may want to use a std::multimap or similar instead of a fixed size array.

Share this post


Link to post
Share on other sites
Steve, under what circumstances would you be likely to want 2 objects to handle the same keypress? And would you not have situations where you'd like 1 object to take precedence over another? (eg. A focused text box should consume your WASD keypresses rather than let them carry on through to the movement system.)

Share this post


Link to post
Share on other sites
Quote:
Original post by Kylotan
Steve, under what circumstances would you be likely to want 2 objects to handle the same keypress?
I was thinking more for a game; there may be reasons where you'd want more than one handler for a keypress - A "configure keys" option could allow the user to bind a key to throw a grenade and issue a taunt at the same time for instance. It's not very likely, but it's easy enough to support.

Quote:
Original post by Kylotan
And would you not have situations where you'd like 1 object to take precedence over another? (eg. A focused text box should consume your WASD keypresses rather than let them carry on through to the movement system.)
That's true - Again I was thinking more in terms of a game where realtime input is required, rather than a UI system.
For a UI system, I'd be inclined to keep track of a single focused UI element, and pass WM_CHAR data (rather than WM_KEYUP / WM_KEYDOWN) data to it, similarly to how Win32 edit controls handle it.

My example was really just answering the "how to handle function calls when a certain key is pressed" part of the OP's post.

Share this post


Link to post
Share on other sites
Quote:
Original post by rostigcykel
how have you people dealt with "input managers"?



Event system. Setup a dictionary/map of input events to delegates/functors (usually using a ui specific helper to determine focused element/clicked on mouse-target). Fairly easy to use, and does a pretty good job at keeping concerns separated.

Share this post


Link to post
Share on other sites
What if you want to throw in a cheat/easter egg so that when the user types in up, up, down, down, right, left, in that order, something occurs? I always thought input systems should be made so they can support whatever you want to do without much, if any modification.
Or would this multi-order input require its own code and array that pushes back key codes, and checks if a specific order of keys is contained?

Share this post


Link to post
Share on other sites
Quote:
What if you want to throw in a cheat/easter egg so that when the user types in up, up, down, down, right, left, in that order, something occurs? I always thought input systems should be made so they can support whatever you want to do without much, if any modification.
Or would this multi-order input require its own code and array that pushes back key codes, and checks if a specific order of keys is contained?
I would make something like that the responsibility of a separate module rather than of the input manager itself. (You could have a module that listened for input events, and then took the appropriate action when certain sequences of events occurred.)

Share this post


Link to post
Share on other sites
Quote:
Original post by Tenac
I always thought input systems should be made so they can support whatever you want to do without much, if any modification.

Everything should be made so that it can support whatever you want to do without much, if any modification.

However, that requires that you have a list of the contents of "whatever you want to do" when you make it.

Share this post


Link to post
Share on other sites
Quote:
Original post by Tenac
What if you want to throw in a cheat/easter egg so that when the user types in up, up, down, down, right, left, in that order, something occurs? I always thought input systems should be made so they can support whatever you want to do without much, if any modification.
Or would this multi-order input require its own code and array that pushes back key codes, and checks if a specific order of keys is contained?


If you were to use Evil Steve's approach then you could add a new listener "CheatCodes", have it listen for all keys. It should have a queue of keys (limited length). Each time it gets a key it should push that key onto the queue. After a new key is added check for strings of keys that match the cheat code.

If a cheat code is say "qwer" and a player pressed t, y, r, q, w, e, r then right after the final r is pressed the queue would look like:

tyrqwer

Do a little search for cheat code "qwer", if its found then apply the cheat. Probably have to empty the queue right after wards or pressing another key will apply the cheat again.

Share this post


Link to post
Share on other sites
Nothing really new w.r.t. the above answers, but perhaps a bit more verbose:

A usual way is to implement a "chain of responsibility". Each entry in that chain is a listener on events, as already mentioned above. The chain is executed from the beginning to its end, so the order matters. This is because a listener must be able to consume an event, i.e. to prevent all listeners below self to be notified.

The CheatCodeListener, as an example, had to be placed relatively close to the beginning of the chain, so that it will notice nearly all input. Of course, it will ever pass the input through, even if it matches a cheat sequence.

A GameMenuListener, as another example, catches the CMD-X buttons, consumes them, and invokes the belonging function.

So the secret is how to order all listeners appropriately. However, this depends on the possibilities of the game. E.g. it may be that the game uses GameState instances like a MenuGameState and a GamePlayGameState. Usually the MenuGameState overrides the GamePlayGameState w.r.t. to input processing. I.e. it consumes any and all input if active. This can be reached by having GameState::onActivation and GameState::onDeactivation routines that install / de-install their input handlers in the global chain.

A GameState need not be restricted to a single handler, of course. Say, it may have its own little chain of handlers. E.g. the GamePlayGameState may have the CheatCodeListener at the top of its own chain, and the AircraftController below it.

Switching the listeners for cases of where the style of playing changes is also possible. E.g. if the player lands the aircraft and alights, the AircraftController can be exchanged with a FirstPersonController or the like.

Listeners like the above can and often need be specialized. E.g. the CheatCodeListener would need to store how much of the proper input sequence is already seen. But that is no problem for objectized listening, of course. Some more general listeners are possible, too. E.g. a general listener for a single key press can be written if the actual action is encoded as parameter.

Share this post


Link to post
Share on other sites
Quote:
Original post by Tenac
I always thought input systems should be made so they can support whatever you want to do without much, if any modification.


I agree. But that is NOT to say that you must design the most uber input system so that nothing new ever needs to be added. It means that you should start by designing the most light weight and most unassuming input abstraction layer.

This layer just manages devices and their state and state transitions. It is not in any way responsible for notifying anyone or game specific logic. It's a system to facilitate polling.

On top of this abstraction layer you can add all the cool notification, chain of responsibility, hierarchial, focusing, ui and gameplay specific input interpretation. But none of that should be in the "input manager" imo, maybe the input interpreter. But it really just comes down to defintions. But the behaviors you guys have been discussing are specific to particular applications.

To reach the goal of Tenac's, there should be very few app specific features in the true input system. It should be portable to any application without bringing over unneeded features, dependencies, or other stripping/workarounds to use it for new functionality.

Share this post


Link to post
Share on other sites
I'm interested in making my own input manager and I am wondering what are people using to get this input at the lowest level ?

I am thinking that some kind of event system is the way to go. Is using just plain windows api good enough or should something like SDL or other library be used ?

I used the Windows API before and found it to be slow.

I don't mean to hijack this thread, just trying to add a little more to it :)

Thanks,

Michael

Share this post


Link to post
Share on other sites
Quote:
I used the Windows API before and found it to be slow.
Hm...you may have been doing something wrong. The Windows API is the basis (either directly or indirectly) for many, many games and other applications, so I think it's pretty safe to say that there's nothing inherently slow about it.

As for SDL, the Windows version uses the Windows API under the hood, so you won't really be getting away from the Windows API by using it. What you'll gain, however, is a degree of platform independence, if that's important to you.

Share this post


Link to post
Share on other sites
Quote:
Original post by mrhodes
I'm interested in making my own input manager and I am wondering what are people using to get this input at the lowest level ?


The highest level I can on a particular platform. Usually, that just means abstracting/adapting some high level input code to some form I deem more useful for that game (which is sometimes nothing for small games). Input handling shouldn't be anywhere close to a performance concern. The focus should be on making something that is easy to deal with.

Share this post


Link to post
Share on other sites
I am planning to replace my DirectInputManager with the Windows API and the solution of Evil Steve seems pretty nice. My knowledge in programming in some points is not the best thats why i am hoping for a little help here.

Maybe somebody can tell me where and how i can implement the example of Steve.

Currently i have a GameManager which handles my gamestate, updates, drawings and device inputs. I would like to add the listening to keys here so i need to replace my current code with something mentioned in this thread. My code looks like this:

void CGameManager::checkKey()
{
switch(inpMan->getInput())
{
case KEY_LEFT: doSomething();
break;
// (...)
}
}


But i dont know which class(es) i should derive from IInputNotifier. Where, when and how do i use the InputManager class? The Foo example doesnt use the AddListener method to make that clear. I cannot derive my GameManager from IInputNotifier, because as soon as i do that the class becomes abstract but i need to instantiate it.

Lets say i create a new class InputManager like in Steve's code. Shouldnt this class derive from the interface?

Besides that, the identifier WPARAM and LPARAM are not known by IInputNotifier but when i include windows.h i get an error PRJ0002.

edit: forget it, i think i solved it

[Edited by - KaaN on November 9, 2009 10:01:29 AM]

Share this post


Link to post
Share on other sites
Maybe I should clarify what I meant by "slow". I was talking about using the message loop and checking for keyup/keydown messages. I always found that if for example I wanted to hold a button, it would register and then there'd be a pause before it would repeat.

For example. I want to press "up" to walk up on a map. The character would move up one space, pause and then continue walking. That's what I meant by slow. I haven't done a lot of work with this type of thing so I imagine there is a better way to do it.

Michael

Share this post


Link to post
Share on other sites
Quote:
Maybe I should clarify what I meant by "slow". I was talking about using the message loop and checking for keyup/keydown messages. I always found that if for example I wanted to hold a button, it would register and then there'd be a pause before it would repeat.

For example. I want to press "up" to walk up on a map. The character would move up one space, pause and then continue walking. That's what I meant by slow. I haven't done a lot of work with this type of thing so I imagine there is a better way to do it.
It sounds like you were probably doing it wrong. I can't be sure without actually seeing the code, but it sounds like you might have been responding to 'key down' events with key repeat enabled. (Typically, for a game you would disable key repeat, and then respond to 'key down' and 'key up' events by modifying a flag or other internal variable.)

Share this post


Link to post
Share on other sites
Quote:
Original post by mrhodes
Maybe I should clarify what I meant by "slow". I was talking about using the message loop and checking for keyup/keydown messages. I always found that if for example I wanted to hold a button, it would register and then there'd be a pause before it would repeat.

For example. I want to press "up" to walk up on a map. The character would move up one space, pause and then continue walking. That's what I meant by slow. I haven't done a lot of work with this type of thing so I imagine there is a better way to do it.
Michael


Sounds like you were handling WM_CHAR messages... try handling WM_KEYDOWN and WM_KEYUP messages instead. Just save the state of the button you are checking, and when you get a WM_KEYDOWN, set the state, and when you get a WM_KEYUP, reset the state. WM_CHAR messages are intended for actual text input where the user doesn't want 30 "a"'s to print just by briefly tapping the "a" key, but may want it to repeat if they hold it down.

[edit] or what jyk said - but even if you ARE using key repeat, you should still be setting and resetting your own key/action state instead - in which case key repeat or not won't really be noticeable any more.

Share this post


Link to post
Share on other sites
I use a different approach such that a certain listener could listen to multiple keys before doing something.

// The concrete implementation of this class is used in WndProc. It just toggles the state of a certain key (key up or down).
class IKeyStateHandler {
public:
virtual ~IKeyStateHandler() {
}

virtual bool isKeyUp( const char& key ) const = 0;

virtual bool isKeyDown( const char& key ) const = 0;
};

// this is equivalent to the listener discussed above
class IKeyProcessor {
public:
virtual ~IKeyProcessor() {
}

virtual void execute( IKeyStateHandler* const stateHandler ) = 0;
};

class KeyInputManager {
public:
KeyInputManager( IKeyStateHandler* const StateHandler )
: stateHandler( StateHandler )
{
// the IKeyStateHandler may be maintained by KeyInputManager itself, it's up to you
}

virtual ~KeyInputManager() {}

void addProcessor( IKeyProcessor* const processor ) {
this->keyProcessorMap.put( processor, processor );
}

void removeProcessor( IKeyProcessor* const processor ) {
this->keyProcessorMap.remove( processor );
}

void runProcessors() {
IIterator<IKeyProcessor*>* const processors = this->keyProcessorMap.getValues();

while( processors->hasMoreElements() ) {
processors->getNextElement()->execute( this->stateHandler );
}
}

private:
Map<IKeyProcessor*, IKeyProcessor*> keyProcessorMap;

IKeyStateHandler* const stateHandler;
};



For a concrete implementation of IKeyProcessor, we could do something like this:

class TurnEntityProcessor : public virtual IKeyProcessor {
public:
// constructor, destructor, stuff

// mandatory override from IKeyProcessor
void execute( IKeyStateHandler* const stateHandler ) {
// continue only if Shift is down
if( stateHandler->isDown( Keys::SHIFT ) ) {
// turn right if D is pressed
if( stateHandler->isDown( Keys::D ) ) {
getEntity()->yaw( RotationDirection::CLOCKWISE );
} else if( stateHandler->isDown( Keys::A ) ) {
// turn left if A
getEntity()->yaw( RotationDirection::COUNTER_CLOCKWISE );
}
}
}
};

// sample usage
IKeyProcessor* const turnProcessor = new TurnEntityProcessor( getPlayerEntity() );

// assuming getKeyInputManager() returns a pointer of KeyInputManager
getKeyInputManager()->addProcessor( turnProcessor );

// memory management is up to you of course

Share this post


Link to post
Share on other sites
Quote:
Original post by clashie
I believe Quake handles WM_CHAR instead of WM_KEYDOWN/UP

Also, what about raw input?
I doubt Quake uses WM_CHAR instead of WM_KEYDOWN/UP for movement. WM_CHAR is sent to your window once, when the key is pressed and that's it - you don't get any indication of when the key is released. They might use a combination of WM_CHAR (for UI, textual input) and GetKeyboardState for movement-related input. But since GetKeyboardState is actually based on your input queue (that is, it gets updated as your application processes the WM_KEYDOWN/UP messages) there's no advantage to using it over WM_KEYDOWN/UP.

Raw input is pretty pointless for keyboard input, but it's useful for mouse input in that you'll get the maximum resolution that is possible for your device. You don't get pointer ballistics applied, which means it would feel wierd if you tried to use raw input for cursor movement, but for FPS games and such, it makes sense.

Personally, my choice for handling input on windows is:
  • WM_KEYDOWN/WM_KEYUP for keyboard movement,
  • WM_CHAR for character input (for UI stuff),
  • WM_MOUSEMOVE/WM_LBUTTONDOWN/etc for UI work (i.e. when you're controlling a cursor),
  • WM_INPUT (aka raw input) for handling camera movement in an FPS-style game and
  • XInput for working with Xbox controllers (though I've never actually implemented this)
I've never bothered with a third-party library to abstract any of that, since it's pretty simple to implement it all yourself (and once it's done, I can use it again in all my subsequent projects). I guess if I ever wanted to port my game to another platform, I might consider using a library to handle it then...

Share this post


Link to post
Share on other sites
Have you ever looked into using OIS?

I've used it on Windows before and it worked good.

Heres what I recently made for input in a simple game with DirectX.

input.h

#pragma once

#define DIRECTINPUT_VERSION 0x0800

#include <dinput.h>

class Input
{

protected:

LPDIRECTINPUT8 m_pDI; // DirectInput object
LPDIRECTINPUTDEVICE8 m_pKeyboard; // DirectInput keyboard device

BYTE m_diks[256]; // Key buffer

public:

Input();
~Input() { cleanup(); }

BYTE* getBuffer() { return m_diks; }

inline bool keyDown(int dikCode) { return ((m_diks[dikCode] & 0x80) == 0x80); }

void update();

bool init(HWND hWnd);
void cleanup();

};


input.cpp

#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dinput8.lib")

#include <windows.h>
#include "input.h"

Input::Input()
{
m_pDI = NULL;
m_pKeyboard = NULL;

for( int i=0; i<256; i++ )
m_diks = 0;
}

void Input::update()
{
HRESULT hr = DI_OK;

// Get the input's device state, and put the state in m_diks
ZeroMemory(&m_diks, sizeof(m_diks));
hr = m_pKeyboard->GetDeviceState(sizeof(m_diks), &m_diks);
if( FAILED(hr) )
{
m_pKeyboard->Acquire();
return;
}
}

bool Input::init(HWND hWnd)
{

// Create a IDirectInput8*
if( FAILED( DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&m_pDI, NULL) ) )
return false;

// Setup the keyboard --

// Create a IDirectInputDevice8* for the keyboard
if( FAILED( m_pDI->CreateDevice(GUID_SysKeyboard, &m_pKeyboard, NULL) ) )
return false;

// Set the keyboard data format
if( FAILED( m_pKeyboard->SetDataFormat(&c_dfDIKeyboard) ) )
return false;

// Set the cooperative level on the keyboard
if( FAILED( m_pKeyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND | DISCL_NOWINKEY) ) )
return false;

// --

// Acquire the keyboard
m_pKeyboard->Acquire();

return true;
}

void Input::cleanup()
{
if( m_pKeyboard )
{
m_pKeyboard->Unacquire();
m_pKeyboard->Release();
m_pKeyboard = NULL;
}

if( m_pDI )
{
m_pDI->Release();
m_pDI = NULL;
}
}

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement