event handling (keypress)
I''m in the process of modularizing a lot of code for a few programs (mostly games that use openGL) & I''d really like to modularize the keypress event handling. Depending on the game state, keypresses mean different things. Instead of checking for whether or not keys are down each frame in the main game loop, I''d like to add keyListeners for various objects, say... the main character would be listening for the keys that would tell it to move or jump & the game state module would listen for pause/resume or help-menu commands. I am aiming towards even more abstraction than this, but I think you get the idea...
I would know exactly how to accomplish this in Java (don''t shoot me, I had to take this one class that uses java), but am clueless for C++. Any tuts or samples out there ?
Whatsoever you do, do it heartily as to the Lord, and not unto men.
How funny. I'm working on the exact same thing, though not with OpenGL, and in C#. I'm not certain about any of this for C++, but I imagine you could capture the event or message (MFC or not) in the main application window. Have an array of function pointers that point to key handlers. Loop through the handlers until one of them processes the key and returns true.
Something like this:
Not very sophisticated, but hopefully you get the idea. As you add objects to your application, you can add their key listeners.
[edited by - variant on March 19, 2004 3:33:16 PM]
Something like this:
typedef bool (*FuncPtr)(char);FuncPtr functionlist[50];AddListener(FuncPtr function){ functionList[count] = function;}ProcessKey(char key){ for(int i=0;i<50;i++) { if((*functionlist)(key)) return; }}
Not very sophisticated, but hopefully you get the idea. As you add objects to your application, you can add their key listeners.
[edited by - variant on March 19, 2004 3:33:16 PM]
Com'on, you're talkting about C++, not C, so use classes:
First provide some interfaces (pure virtual classes, this
means one or more methods have the "= NULL" extension which
means it is NOT implemented here -> pure virtual):
Ok, now we have the basics required for our event handling
system. Lets see how an event handler should look like:
Remarks: Fifo
first-in-first-out: first event triggered should be the first handled (i.e. a ring buffer)
Remarks: Listener List
instead of a normal C/C++ pointer list for the listeners
one could use a double linked list like the stl list
(important if the listeners often register and unregister)
I hope you have a slight idea of how it should work. I have
implemented this for my OpenGL framework and it works fine. I
use this as well for sending rendering and AI events as well
as for keyboard and mouse events ( = actions ).
If you have further questions, I can send you some working
source.
Lord Jake
x = rx ( 1 - x ) ; r > 3.5699
... and chaos may begin ...
[edited by - Lord_Jake on March 24, 2004 9:03:54 AM]
First provide some interfaces (pure virtual classes, this
means one or more methods have the "= NULL" extension which
means it is NOT implemented here -> pure virtual):
// our event class - provide only the event id - all other// required stuff must be implemented in a sub class derived // from this virtual base classclass CEvent{public: virtual int getEventID ( ) const = NULL;};// now we need a listener. This is only important because// instead of using a C callback function we use this class// to pass an event to it's receiveEvent method// all classes which want to listen to an event must be// derived from this classclass CListener{public: virtual void receiveEvent ( CEvent* pEvent ) = NULL;}
Ok, now we have the basics required for our event handling
system. Lets see how an event handler should look like:
// the event handler properties:// - should have a list of listeners// - able to add/remove listeners// - it should be possible to call this handler from everywhere// so one can trigger an eventclass CEventHandler{ int m_nListenerCount; // number of listeners CListener** m_pListenerList; // list with listeners int m_nEventFifoStart; // first queued event int m_nEventFifoEnd; // first free position CEvent** m_pEventFifo; // event fifopublic: // allocates data - good style means to do this NOT in // the constructor (-> here you can check for errors!) bool initEventHandler ( ); // every listener registeres here (means: the listener // is added to the listeners list in the event handler bool registerListener ( CListener* pListener ); // remove a listener from the listeners list bool unregisterListener ( CListener* pListener ); // call this to send an event to all listeners - can be // i.e. a windows message in the WinProc function // note: it's up to you if you handle events immidiately // or queue them (see performEvents ()) void triggerEvent ( CEvent* pEvent ); // if you queue your events call this in your main app loop // to handle the queued events void performEvents ( );};
Remarks: Fifo
first-in-first-out: first event triggered should be the first handled (i.e. a ring buffer)
Remarks: Listener List
instead of a normal C/C++ pointer list for the listeners
one could use a double linked list like the stl list
(important if the listeners often register and unregister)
I hope you have a slight idea of how it should work. I have
implemented this for my OpenGL framework and it works fine. I
use this as well for sending rendering and AI events as well
as for keyboard and mouse events ( = actions ).
If you have further questions, I can send you some working
source.
Lord Jake
x = rx ( 1 - x ) ; r > 3.5699
... and chaos may begin ...
[edited by - Lord_Jake on March 24, 2004 9:03:54 AM]
x = rx ( 1 - x ) ; r > 3.5699... and chaos may begin ...
Thanks variant for your reply, I can tell you have though that through very well in C#. & THANK YOU JAKE for your C++ explanation, that is more along the lines of what I am looking for. Perhaps we can get a bit more detailed... I''m going to rattle off a bunch of crap... & if you have the time, could you stop me & explain if I''m wrong or could do it a better way?
So in a broad sense... I would create a CEventHandler object upon initialization & in the windproc function (where else?) I would decode the events that windows passes me, & for every key/mouse event create an instance of a CEvent (mouse or key) & pass that event to the handler by calling triggerEvent(). The handler than does the rest, depending on specifics of implementation. fyi- I don''t think I will do Queueing of key/mouse events, because I would like the program to be VERY responsive to the user''s actions (does that sound wise?).
Now say for instance the only 2 events I''m worried about right now are KEY events (key presses) & MOUSE events (down,drag,up,move,clicked). The rest of the events can be handled as they are being handled right now. So I should design 2 classes that both derive from the "CEvent" class, for example the mouse class would contain a variable for mouse coordinates & the key class would contain a variable for what key was pressed.
Does this sound good... all of the widget classes in my GUI package (button, text line edit, num line edit, slider) could then inherit from the CListener class & they would each implement their own receiveEvent() method. For instance, if the user hits the ''N'' key & a button on the current panel has ''N'' for its hotkey, then when it receives the event, it could swallow the event & perform the action that it was created to do, perhaps.... start a "New game".
I would say more now... but I think I need to sit down with a pencil & a pad & start hacking out my own ideas... thanks for the start.
last Q - is the popular place to trigger key/mouse events from inside the winproc() function????
So in a broad sense... I would create a CEventHandler object upon initialization & in the windproc function (where else?) I would decode the events that windows passes me, & for every key/mouse event create an instance of a CEvent (mouse or key) & pass that event to the handler by calling triggerEvent(). The handler than does the rest, depending on specifics of implementation. fyi- I don''t think I will do Queueing of key/mouse events, because I would like the program to be VERY responsive to the user''s actions (does that sound wise?).
Now say for instance the only 2 events I''m worried about right now are KEY events (key presses) & MOUSE events (down,drag,up,move,clicked). The rest of the events can be handled as they are being handled right now. So I should design 2 classes that both derive from the "CEvent" class, for example the mouse class would contain a variable for mouse coordinates & the key class would contain a variable for what key was pressed.
Does this sound good... all of the widget classes in my GUI package (button, text line edit, num line edit, slider) could then inherit from the CListener class & they would each implement their own receiveEvent() method. For instance, if the user hits the ''N'' key & a button on the current panel has ''N'' for its hotkey, then when it receives the event, it could swallow the event & perform the action that it was created to do, perhaps.... start a "New game".
I would say more now... but I think I need to sit down with a pencil & a pad & start hacking out my own ideas... thanks for the start.
last Q - is the popular place to trigger key/mouse events from inside the winproc() function????
Whatsoever you do, do it heartily as to the Lord, and not unto men.
Yes, you got it right - except for creating the class in the WinProc function.
If you want global access to a class a very common thing is to add a static method called "getInstance" which internally will create an instance of that class if it does not exist and return a reference to that class.
In your normal init function of your application you just add
a call your global class'' init method.
About the CEvents for mouse and keyboard. You could also create a CWindowsEvent class which passes the LPARAM and WPARAM of the windows message - thats up to you.
Oh, two more things to think about:
- Multiple modes:
If you think about an Input Event Handler for keyboard and mouse, think about adding special "modes" for which the listeners are registered. These modes could be a "menu" mode and a "game" mode for example. Sure you want to react different on a mouse click in menu mode than in game mode. You could create a listener list for every mode and just send to the list of the current mode (-> you wont need to register/unregister every time your game switches between menu and normal game).
- instant events:
As you wrote you want to pass your keyboard and mouse events directly without queuing. You can create a second triggering method "triggerInstantEvent( CEvent* )" to trigger those keyboard and mouse events to be handled immidiately and use the other method for normal events which can be queued. Queuing can be very useful if you want to split the cpu time your program has (i.e. one part rendering, one part game actions, on part artificial intelligence).
x = rx ( 1 - x ) ; r > 3.5699
... and chaos may begin ...
If you want global access to a class a very common thing is to add a static method called "getInstance" which internally will create an instance of that class if it does not exist and return a reference to that class.
In your normal init function of your application you just add
a call your global class'' init method.
class MyGlobalClass{protected: // define constructor and copy constructor protected so // that they cannot accessed from outside the class // -> ensures that the only way is to call getInstance () // and there really only exists ONE instance of it MyGlobalClass ( ); MyGlobalClass ( const MyGlobalClass& );public: // destructor still must be public: virtual ~MyGlobalClass ( ); // define the static access method static MyGlobalClass& getInstance ();};// implementation of getInstance is very simple:// return value: reference to the global instanceMyGlobalClass& MyGlobalClass::getInstance (){ // first access here the global (static) instance // is created static MyGlobalClass g_aInstance; // because g_aInstance is static we can return a // reference to it (-> it is NOT local!) return g_aInstance;}// and in your app''s initbool CMyApp::init (....param....){ ... // add a call to the init function of your global // class - this first call will create the instance // as well MyGlobalClass::getInstance ().init (); ...}// btw, this would be a thing for macros// GET_MY_GLOBAL_CLASS().triggerEvent ();#define GET_MY_GLOBAL_CLASS MyGlobalClass::getInstance
About the CEvents for mouse and keyboard. You could also create a CWindowsEvent class which passes the LPARAM and WPARAM of the windows message - thats up to you.
Oh, two more things to think about:
- Multiple modes:
If you think about an Input Event Handler for keyboard and mouse, think about adding special "modes" for which the listeners are registered. These modes could be a "menu" mode and a "game" mode for example. Sure you want to react different on a mouse click in menu mode than in game mode. You could create a listener list for every mode and just send to the list of the current mode (-> you wont need to register/unregister every time your game switches between menu and normal game).
- instant events:
As you wrote you want to pass your keyboard and mouse events directly without queuing. You can create a second triggering method "triggerInstantEvent( CEvent* )" to trigger those keyboard and mouse events to be handled immidiately and use the other method for normal events which can be queued. Queuing can be very useful if you want to split the cpu time your program has (i.e. one part rendering, one part game actions, on part artificial intelligence).
x = rx ( 1 - x ) ; r > 3.5699
... and chaos may begin ...
x = rx ( 1 - x ) ; r > 3.5699... and chaos may begin ...
One remark:
If you use the code above you have to know that the instance will be created the first time you call getInstance () and will be release on the exit of your program.
If you want to create it and destroy it at your will, you will need to do this by a pointer.
x = rx ( 1 - x ) ; r > 3.5699
... and chaos may begin ...
If you use the code above you have to know that the instance will be created the first time you call getInstance () and will be release on the exit of your program.
If you want to create it and destroy it at your will, you will need to do this by a pointer.
// global pointer (-> set to NULL at start, IMPORTANT)MyGlobalClass* g_pMyGlobalClass = NULL;class MyGlobalClass:{protected: ...define ctor and cctor...public: static MyGlobalClass* globalGetOrCreate (); static void globalFree ();};// implementation:MyGlobalClass* MyGlobalClass:globalGetOrCreate (){ // check if instance already exists if (g_pMyGlobalClass == NULL) { // no, create instance // ctor can be accessed from inside the class'' implementation g_pMyGlobalClass = new MyGlobalClass (); } // return pointer to this class return g_pMyGlobalClass;}void MyGlobalClass::globalFree (){ // check if we have an instance if (g_pMyGlobalClass != NULL) { // delete instance delete g_pMyGlobalClass; // set back to zero (IMPORTANT) g_pMyGlobalClass = NULL; }}
x = rx ( 1 - x ) ; r > 3.5699
... and chaos may begin ...
x = rx ( 1 - x ) ; r > 3.5699... and chaos may begin ...
March 25, 2004 09:18 AM
Sorry if I''m incorrect in this, but I believe CEvent is MFC which is kind of incongruous with the wndproc approach. Maybe instead you could define a user message and post to it:
#define MY_KEY_MESSAGE (WM_USER + 0x234)#define MY_MOUSE_MESSAGE (WM_USER + 0x235)//...in your wind proc, when you receive the mouse// and key messages, have a mechanism to// loop through your listenersPostMessage(Listener->hWnd,MY_KEY_MESSAGE,someWParam,someLParam);//...The params could be pointers to structures containing// your data...//Or create a class that you pass the params to that posts the//messages for you in a similar manner.[\code]I don''t know if there is an event structure purely for Win32. Sorry if this is wrong or useless!
March 25, 2004 09:24 AM
Sorry...Didn''t read close enough to realize that CEvent is not the MFC version!!
I think I just wrote that little poorly, I didn''t mean that I would instantiate my eventHandler object inside of the windproc() function, but rather I meant that thats where I would decode the key/mouse events & pass them to my handler with the triggerEvent() call. The thing you described... about creating only one instance, is that called a Singleton ?
-modes-
yes yes yes... I was just gonna ask... "what would be a good way to switch between modes?" (game mode & menu mode)... I was pondering the idea of adding & removing listeners every time, but I think what you said about having 2 distinct list of listeners would be better.
In fact, wouldn''t it a be a good idea to just have 2 seperate event handler objects, one for game mode & one for menu mode (& perhaps others for other modes). Since we are going for modular, the purpose of the event handler itself is just to traffic events to all of its listeners (& maybe perhaps distinguish between the listener with the focus & other listeners, like... a highlighted menu item would have focus). I don''t know what I think about the event handler itself having 2 seperate lists... because then it relies on knowledge of the outside world to perform its task (the knowledge of what mode you are in). But I suppose you would have to manage the mode somewhere... whether you manage the lists in the eventhandler, or whether you manage different eventhandlers outside.
Question on organization for the game state/mode. The user should begin the program in menu mode & should be able to go back & forth between game/menu mode rather easily. Certain menu selections could either
A) alter the game state (ex: diff level) & thus begin a new game.
B) change settings (ex: sound level) & then resume the game.
C) others such as quit or go to high-score screen.
Would it be a good idea to have a game class, that contains a level class, entity classes for each entity, yada yada yada...
So some of the event listeners (such as these menu items) would have to have some sort of access to the game state, & then whenever a menu control is activated that puts us back into game mode, the game would be loaded : level, sounds, starting positions, entity controls (add listeners based on control settings).
say for now I only have 2 game modes.
So maybe this....
There is a MOTHER_CLASS that passes itself to some of the event listeners & all that the event listeners know about this MOTHER_CLASS is that is has such methods as:
activateMode(modeType MODE);
& inside methods like this one, the MOTHER_CLASS performs such operations as:
switching eventHandlers (game/menu),
clearing the event Queue in the handler
(in case it had some from last time it ran)
loading graphics & sound (game or menu stuff)
blah blah blah
I can almost picture organizing it like this, that way the mother class does all the thinking for the various modes, & it passes itself to the listeners that it wants to, so that they can tell the mother class to perform certain methods like activateMode().
-modes-
yes yes yes... I was just gonna ask... "what would be a good way to switch between modes?" (game mode & menu mode)... I was pondering the idea of adding & removing listeners every time, but I think what you said about having 2 distinct list of listeners would be better.
In fact, wouldn''t it a be a good idea to just have 2 seperate event handler objects, one for game mode & one for menu mode (& perhaps others for other modes). Since we are going for modular, the purpose of the event handler itself is just to traffic events to all of its listeners (& maybe perhaps distinguish between the listener with the focus & other listeners, like... a highlighted menu item would have focus). I don''t know what I think about the event handler itself having 2 seperate lists... because then it relies on knowledge of the outside world to perform its task (the knowledge of what mode you are in). But I suppose you would have to manage the mode somewhere... whether you manage the lists in the eventhandler, or whether you manage different eventhandlers outside.
Question on organization for the game state/mode. The user should begin the program in menu mode & should be able to go back & forth between game/menu mode rather easily. Certain menu selections could either
A) alter the game state (ex: diff level) & thus begin a new game.
B) change settings (ex: sound level) & then resume the game.
C) others such as quit or go to high-score screen.
Would it be a good idea to have a game class, that contains a level class, entity classes for each entity, yada yada yada...
So some of the event listeners (such as these menu items) would have to have some sort of access to the game state, & then whenever a menu control is activated that puts us back into game mode, the game would be loaded : level, sounds, starting positions, entity controls (add listeners based on control settings).
say for now I only have 2 game modes.
So maybe this....
There is a MOTHER_CLASS that passes itself to some of the event listeners & all that the event listeners know about this MOTHER_CLASS is that is has such methods as:
activateMode(modeType MODE);
& inside methods like this one, the MOTHER_CLASS performs such operations as:
switching eventHandlers (game/menu),
clearing the event Queue in the handler
(in case it had some from last time it ran)
loading graphics & sound (game or menu stuff)
blah blah blah
I can almost picture organizing it like this, that way the mother class does all the thinking for the various modes, & it passes itself to the listeners that it wants to, so that they can tell the mother class to perform certain methods like activateMode().
Whatsoever you do, do it heartily as to the Lord, and not unto men.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement