简体   繁体   中英

How does WM_KEYDOWN process different keys?

This is a rather unusual question, but I need some explanation on exactly how Win32 handles different keydown events.

WM_KEYDOWN is used when you want to receive long presses, eg if you're in a game, and you want to move forward, you press UP arrow key, or 'W' key, and the character in the game moves on. You do not have to press the keys multiple times, because of WM_KEYDOWN .

I have a 3D OpenGL application, based on win32, in which I am using both 'WASD' and arrow key to move around in the 3D world. They both work exactly how they're supposed to work.

Being said that, when I am in the 3D world, I can sense there is a subtle difference between the way Win32 handles WASD and arrow keys.

    case UP_k: 
    f_zPosition-=CAM_MOVEMENT_SPEED; 
     break; 

The same code is also used for case 'W': .

But in the game, for some reason the movement done by arrow keys is much smoother than the movement of WASD keys.

  1. How does Win32 handles these events internally?
  2. How can I make the movement by WASD smooth like arrow keys.

I wonder if anyone has ever noticed this in Win32, here is my event loop code, as per requested in the comments.

do
{
    window.displayGL();
    PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
    TranslateMessage(&msg);
    DispatchMessage (&msg);

}while(msg.message != WM_QUIT);

A normal message proccesing loop on a win32 applications calls two functions:

TranslateMessage(&msg);
DispatchMessage(&msg);

The first function, TranslateMessage looks at virtual key messages (that is WM_KEYDOWN messages for example) and then adds character messages to the message queue if the pressed key corresponds to a character key. If you keep the 'W' key pressed, a lot of WM_KEYDOWN are generated because of the autorepeat feature. The same goes for the arrow keys, but in the case of the 'W ', for every WM_KEYDOWN a WM_CHAR is also generated, which your application will have to process.

Even if your application just looks at the WM_CHAR and discards them, the resulting delay may be noticeable, especially because you move the camera each time you process a WM_KEYDOWN

For more info check this .

The solution:

You should make your movement speed independent of the speed at which you process your input. There are two main ways to do that:

  • Fixed framerate: Update your screen a fixed number of times per second. On each update move the camera/player the same distance.
  • Timed frames Update your screen as many times as you can, but measure the time between iterations. When you move, multiply the movement speed by the time since last update.

In both cases, don't move your character when you process the input. Instead, when you get a WM_KEYDOWN set a boolean playerIsMoving to true , and reset it to false when you get a WM_KEYUP . Then apply the movement when you generate your next frame only if the variable is in true .

[UPDATE]

After seeing your main loop: Try modifying it like this:

Modification 1 : Comment out TranslateMessage . You don't need it unless you want to have textboxes and the like in your app.

Modification 2 : Add a separate function to update world state.

do
{
    UpdateWorld()
    window.displayGL();
    PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
    //TranslateMessage(&msg);
    DispatchMessage (&msg);

}while(msg.message != WM_QUIT); 

// I won't define exactly all of this, you can get an idea 
// what I'm doing by the names. playerIsMoving should be set
// and reset in the `WindowProcedure` when processing 'WM_KEYDOWN/UP':
void UpdateWorld(){
    float delta = timeSinceLastCall()
    if(playerIsMoving)
        player.pos += speed * delta;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM