Input Manager

Started by
20 comments, last by eFoDay 14 years, 5 months ago
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]
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.
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.)
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.
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.
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?
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.)
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.
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.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

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.

This topic is closed to new replies.

Advertisement