0
\$\begingroup\$

When I was looking on how to handle key inputs in a game loop I came across the following pattern: All native key events (up or down) get queued in an event queue that is later processed. The outcome is an array, that says for each key if the key is down or up. Pretty straight forward. I then took a look in the DOOM 3 BFG source code and it seems, they do exactly the same:

DOOM-3-BFG/neo/sys/win32/win_wndproc.cpp

LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) {
  int key;
  switch( uMsg ) {
      // ...
      case WM_KEYDOWN:
          // ...
          Sys_QueEvent( SE_KEY, key, true, 0, NULL, 0 );
          break;

      case WM_SYSKEYUP:
      case WM_KEYUP:
          // ...
          Sys_QueEvent( SE_KEY, key, false, 0, NULL, 0 );
          break;
      // ...
  }
  //...
}

DOOM-3-BFG/neo/framework/EventLoop.cpp

void idEventLoop::ProcessEvent( sysEvent_t ev ) {
  // track key up / down states
  if ( ev.evType == SE_KEY ) {
      idKeyInput::PreliminaryKeyEvent( ev.evValue, ( ev.evValue2 != 0 ) );
  }
  // ...
}

DOOM-3-BFG/neo/framework/KeyInput.cpp

void idKeyInput::PreliminaryKeyEvent( int keynum, bool down ) {
  keys[keynum].down = down;
}

I then started asking myself, what happens if a frame might take longer and the key down and up events happen in the same frame? Because that actually means that these events compete each other e.g. cancel each other out. How can this situation be handled or is this scenario uncommon?

\$\endgroup\$
1
\$\begingroup\$

"What happens" depends entirely on how you process the keyboard event queue you're producing.

If, during the processing of the queue, you dispatch handling for both the key down and the key up event, nothing interesting happens: the things you do on key-down and on key-up still happen normally, they just happen with less of a delay between them.

If you instead process the queue once a frame and resolve it into a "current keyboard state", then the key state will probably, as you note, "cancel out" resulting in the key state showing "not pressed." Chances are, however, if all you're doing with the queue is turning it into a current state you're wasting your time (you could use the APIs that feed the queue to do that directly with no loss of functionality).

If, however, you're using the using the queue to get a higher-level keyboard state for the frame, one that for example distinguishes between a key being held down versus tapped, for example, you'll want be tracking event times in the queue so you know how long it was between key down and key up. If it was short enough, consider that a key "tap" and dispatch accordingly. If it was longer, maybe consider it "held" for the purposes of the frame.

If you just doing the second thing and want to avoid losing the key state for events that occur in a single frame, tracking the down/up time can let you do that as well.

\$\endgroup\$
  • \$\begingroup\$ You say, that I could use the API that feeds the queue directly. But then I have a problem with synchronization because the events may occur at any time (for example when I am half way through the update process). Its further interesting, that id Tech (DOOM 3) processes the whole queue at once each frame into a "current keyboard state". \$\endgroup\$ – Sam Nov 17 '15 at 23:44
  • \$\begingroup\$ Yes, that is a downside of updating the state directly. None of these options is perfect; feeding a queue and processing it into a state later introduces a time delay to the input, for example. The best option will depend on what advantages you want and what disadvantages you can live with. \$\endgroup\$ – Josh Nov 18 '15 at 0:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.