Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Problem with unresponsive event managing

This thread is locked; no one can reply to it. rss feed Print
Problem with unresponsive event managing
Øyvind Andersson
Member #14,671
November 2012

Hi!

I have a problem with my game loop, or the event handling. The loop or allegro seems to miss out on certain events, which is mostly notices on keyboard (up/down) events.

I have created a class for the keyboard, that stores key presses (key[true|false]) to enable IsKeyPressed(int key), for i.e. move player on screen, and such.

The problem though - although not every time - is that when I press for example UP and LEFT at the same time, either LEFT or UP does not receive the KEY_UP state when I release one of them, thus making the character move in that direction until I press it again to re-send the UP event.
I hope you're still with me.. I'm not sure exactly what is causing it, but I can imagine the game loop, or the event manager and it's calling stack might be too much?

Here is a few details and some code:

Timer speeds

#SelectExpand
1const float DRAW_FPS = 30.0f; 2const float UPDATE_TICK = 60.0f;

Timer init

#SelectExpand
1m_pDrawTimer = new CGameTimer(1.0 / DRAW_FPS); 2m_pUpdateTimer = new CGameTimer(1.0 / UPDATE_TICK); 3m_pGameTimer = new CGameTimer(1.0); 4 5/**** 6 In CGameTimer ctor 7****/ 8CGameTimer::CGameTimer(double speed_seconds) 9{ 10 m_pTimer = NULL; 11 m_pTimer = al_create_timer(speed_seconds); 12} 13 14... 15 16/* 17 Declared in CGameTimer header 18*/ 19CTimer *GetTimer() { return m_pTimer; } 20CTimer *m_pTimer; // CTimer = typedef ALLEGRO_TIMER CTimer

Game loop

#SelectExpand
1m_pGameTimer->StartTimer(); // Records our in-game time 2 m_pDrawTimer->StartTimer(); 3 m_pUpdateTimer->StartTimer(); 4 m_dPrevGameTime = m_pGameTimer->GetTime(); 5 6 /********************************* 7 * 8 * Run the game loop! 9 * 10 **********************************/ 11 m_bIsRunning = true; 12 while(m_bIsRunning) 13 { 14 WaitForEvent(&g_Event); 15 16 /* 17 Update when the timer has hit, and if the game is enabled 18 */ 19 if( m_bShouldUpdate && m_bIsEnabled ) 20 { 21 /* 22 Update input devices 23 */ 24 g_InputManager->UpdateInputDevices(g_Event); 25 26 /* 27 Update game components 28 */ 29 Update(m_pGameTimer); 30 } 31 32 /* 33 Draw when the draw timer has hit, and when the event queue is empty, 34 to make sure we rather update user input than render a frame. 35 @todo Pause game if focus is lost 36 */ 37 if( m_bShouldDraw && CEventManager::IsEventQueueEmpty(GetQueue()) ) 38 { 39 PreDraw(); // Clear buffer and other pre-proc 40 Draw(); // Overridable, so no critical buffer operations here. Hence pre/post. Draw gamecomponents 41 PostDraw(); // Swap buffers, calc fps 42 } 43 44 /* 45 When the display is closed, we initiate end game procs. 46 @todo Change state to Quit, and handle savegame stuff.. 47 */ 48 if(g_Event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) 49 { 50 EndGameLoop(); 51 } 52 53 if( g_Event.type == ALLEGRO_EVENT_TIMER ) 54 { 55 if( g_Event.timer.source == m_pUpdateTimer->GetTimer() && m_bShouldUpdate == false) 56 { 57 m_bShouldUpdate = true; 58 } 59 if( g_Event.timer.source == m_pDrawTimer->GetTimer() && m_bShouldDraw == false) 60 { 61 m_bShouldDraw = true; 62 } 63 } 64 } 65 66 67.... 68 69/* 70 Here is the Update function called within StartGameLoop() 71*/ 72void CBaseGame::Update(CGameTimer *pGameTime) 73{ 74 /* 75 Update our game components 76 */ 77 for( vGameObjectIterator iter = m_GameObjectCollection.begin(); iter != m_GameObjectCollection.end(); iter++) 78 { 79 CBaseGameObject *ent = (*iter); 80 if( ent->IsEnabled() && ent->GetShouldUpdate() ) 81 { 82 ent->Update(pGameTime); 83 } 84 else 85 { 86 ent->SetShouldUpdate(true); 87 } 88 } 89 90 m_bShouldUpdate = false; 91}

Here are parts of CEventManager functions that are used in the loop.
The CBaseGame class (owns the game loop fuction) is derived from CEventManager btw.

#SelectExpand
1void CEventManager::WaitForEvent(CEvent *ev) 2 { 3 if( !m_pEventQ ) 4 { 5 g_Log->ConPrint(0, "%qf Error!: Unable to poll events! Event queue is NULL!", "CEventManager", "WaitForEvent(CEvent *ev)"); 6 } 7 al_wait_for_event(m_pEventQ, ev); 8 } 9 10... 11 12bool CEventManager::IsEventQueueEmpty(CEventQueue *queue) 13 { 14 return al_is_event_queue_empty(queue); 15 }

And finally, here are the keyboard device functions used to get keypress stuff.
First functions are from the InputManager that handle input devices

#SelectExpand
1void CInputManager::UpdateInputDevices(CEvent &ev) 2{ 3 vInputDeviceIterator iter; 4 for( iter = m_vDevices.begin(); iter != m_vDevices.end(); iter++ ) 5 { 6 /* 7 Update device, if it's initialized and installed 8 */ 9 if( (*iter)->IsInitialized() && (*iter)->IsInstalled() ) 10 { 11 (*iter)->UpdateInput(ev); 12 } 13 } 14} 15 16CKeyboardState *CInputManager::GetKeyboard() 17{ 18 if( !m_pKeyboard || !m_pKeyboard->IsInitialized() || !m_pKeyboard->IsInstalled() ) 19 return NULL; 20 21 return m_pKeyboard; 22} 23 24 25void CKeyboardState::UpdateInput(CEvent &ev) 26{ 27 m_ev = ev; 28 if( ev.type == ALLEGRO_EVENT_KEY_DOWN ) 29 { 30 m_bKeys[ev.keyboard.keycode] = true; 31 } 32 33 if( ev.type == ALLEGRO_EVENT_KEY_UP ) 34 { 35 m_bKeys[ev.keyboard.keycode] = false; 36 } 37 38 /** 39 * @todo - Check for modifiers... 40 */ 41} 42 43 44bool CKeyboardState::IsKeyPressed(int key) 45{ 46 return m_bKeys[key]; 47}

Ok. A lot of code, but hopefully organized well enough. I think that is most of the parts that is part of updating, getting events, checking keyboard input etc etc.

Here is a rough breakdown of a call-stack to get keyboard input from the game's update function:

StartGameLoop
UpdateInputDevices(g_Event)
InputDevice->UpdateInput(ev) ( this is called every UPDATE tick in the game loop )
IsKeyPressed(key) ( This would be called in any Update() function from the game object collection that is a vector member of the game class that has the game loop )

So, are there any off-the-bat errors and/or grave speed issues? It runs fast and upkeeps 30 FPS at all times. Also a sidenote: I just changed the update speed to 60.0f. It has always been at 30.0f, but the problem did not change.

The higher I set it, the more event problems occurs (I set it to 80.0f to test, and it missed almost every key up/down event. Very hacky behaviour).

I hope you can help, as I don't want to continue very far before I have solved this.
Thank you! :)

Slartibartfast
Member #8,789
June 2007
avatar

If I understand your code correctly, after every time you update, you call Update which sets m_bShouldUpdate to false. m_bShouldUpdate stays false until the next timer event. Once m_bShouldUpdate is true and an event happens, you set it to false and again only set it back to true once a timer event happens.
This means that you can only detect one event between timer events (because as soon as you detect an event you set m_bShouldUpdate to false and no longer call your UpdateInputDevices function).

Øyvind Andersson
Member #14,671
November 2012

Holy shit....

Well that was easy, hehe.
I did this instead, to keep Updates on gameobjects themselves, at a controlled rate:

#SelectExpand
1/* 2 Update input devices 3 */ 4 g_InputManager->UpdateInputDevices(g_Event); 5 6 /* 7 Update when the timer has hit, and if the game is enabled 8 */ 9 if( m_bShouldUpdate && m_bIsEnabled ) 10 { 11 /* 12 Update game components 13 */ 14 Update(m_pGameTimer); 15 }

Thanks mate, that fixed it! :)

Go to: