Input Manager

Started by
20 comments, last by eFoDay 14 years, 5 months ago
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.
Advertisement
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
Michael RhodesTiger Studios Web Designhttp://tigerstudios.net
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.
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.
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]
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
Michael RhodesTiger Studios Web Designhttp://tigerstudios.net
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.)
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.
I believe Quake handles WM_CHAR instead of WM_KEYDOWN/UP

Also, what about raw input?
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 aboveclass 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 usageIKeyProcessor* const turnProcessor = new TurnEntityProcessor( getPlayerEntity() );// assuming getKeyInputManager() returns a pointer of KeyInputManager getKeyInputManager()->addProcessor( turnProcessor );// memory management is up to you of course

This topic is closed to new replies.

Advertisement