Hi everyone.

For the past day I've been trying to find a good way to handle keyboard input.

Naturally, I wanted to just use WM_KEYDOWN and WM_KEYUP to handle my events, but they proven to have limitations. In my game there was a keyboard delay. If you pressed and held down the right arrow key, the character would move one pixel to the right, stop, then continue moving. I wanted the character to immediately start moving and continue moving once the right arrow key was pressed and held.

Another problem with using just WM_KEYDOWN is that if you are moving your character with the right arrow key and then press spacebar, your character stops moving. Apparently this WM_KEYDOWN message couldn't handle more than one key being processed at a time.

I look into DirectInput and get it working finally. I notice there is none of that keyboard repeat rate delay and it could handle more than one key being held at a time. The problem was, it was messy. Basically, DirectInput was overkill. Also, handling KeyUp messages with DirectInput was hell.

Now I look into GetAsyncKeyState. It looks good, but it seems a bit messy and slow.


Overall, I'm looking for a way to use WM_KEYDOWN and WM_KEYUP to handle more than one key at a time (so if you're moving and press space you don't stop moving for example) and I also want it to not have a delay. For example, the instant I press the right arrow key my character starts moving and doesn't stop.


The only method I found for doing this so far required a messy array of bools I didn't really want to get into. It was worse than using DirectInput or GetAsyncKeyState in my opinion.


Thanks

Dani AI

Generated

A reliable, simple pattern is to treat WM_KEYDOWN/WM_KEYUP as state setters (not movement events) and drive movement from the game loop each frame. That covers the delay and multi-key problems reported and keeps the spirit of ’s toggle idea while avoiding XOR toggles that can get out of sync.

Keep a compact key-state container (std::bitset or a 256-byte array). In WM_KEYDOWN set the key, but ignore auto-repeat using the lParam previous-state bit; in WM_KEYUP clear the key. Reset the whole state on focus loss so missing WM_KEYUP messages don’t leave keys stuck. Then, each frame, move the character based on the current key bits and a delta-time multiplier for smooth, frame-rate independent motion.

Example (conceptual):

static std::bitset<256> keys;

LRESULT CALLBACK WndProc(...) {
  switch(msg) {
    case WM_KEYDOWN:
      if ((lParam & (1 << 30)) == 0) // not an auto-repeat
        keys.set((unsigned char)wParam);
      break;
    case WM_KEYUP:
      keys.reset((unsigned char)wParam);
      break;
    case WM_KILLFOCUS:
      keys.reset(); // avoid stuck keys on alt-tab
      break;
  }
}

while (running) {
  // pump messages via PeekMessage/DispatchMessage
  // compute dt (seconds since last frame)
  if (keys.test(VK_RIGHT)) pos.x += speed * dt;
  if (keys.test(VK_LEFT))  pos.x -= speed * dt;
  if (keys.test(VK_SPACE)) /* jump or fire */;
  render();
}

Notes and gotchas:

  • Ignore WM_CHAR for game input; use virtual-key codes.
  • Hardware key rollover/ghosting can prevent some simultaneous keys—test on another keyboard if multiple keys still fail.
  • Prefer a game loop driven by a high-resolution timer to Windows timers for smoother motion.
  • If extremely low-level timing or raw HID scancodes are required, consider Raw Input; for most games the state-array + frame update approach is simplest and robust.

Use WM_KEYDOWN as a toggle instead of an event that actually moves your character. You've probably discovered by now that your character movement corresponds to how repeat keys work. Therefore;

MoveFlag ^= 1;

Then in a timer event check if the flag is on and if so move your character.

if (MoveFlag && 1)
   .... Move Character

If you want to move in all four directions or even diagonally then just use a bit position in MoveFlag array to represent up down left right. If you use a 4 bit or 8 bit values in MoveFlag you can even change the speed you want your character to move in that direction relative to the rest.

So lets say MoveFlag represents Left Up Right Down each having a 4 bit value. Then MoveFlag = 0460H. This would move your character 4 pixels up and 6 right everytime your timer event was executed. Of course there is a lot of code that needs to be implemented to make this work, but I think you understand now how using WM_KEYDOWN, WM_KEYUP as toggles and then a timer works much better for your objective.

NOTE: I code exclusively in assembly so my C++ syntax may not be correct, but procedurally this is how I would implement.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.