WM_KEYDOWN 如何处理不同的键?

Posted

技术标签:

【中文标题】WM_KEYDOWN 如何处理不同的键?【英文标题】:How does WM_KEYDOWN process different keys? 【发布时间】:2014-05-26 04:41:25 【问题描述】:

这是一个相当不寻常的问题,但我需要解释一下 Win32 如何处理不同的按键事件。

WM_KEYDOWN 用于接收长按,例如如果您在游戏中,并且想要前进,请按向上箭头键或“W”键,游戏中的角色就会继续前进。 由于WM_KEYDOWN,您不必多次按键。

我有一个基于 win32 的 3D OpenGL 应用程序,我在其中使用 'W-A-S-D' 和 arrow key 在 3D 世界中移动。它们都完全按照应有的方式工作。

话说回来,当我身处 3D 世界时,我能感觉到 Win32 处理 WASD 和箭头键的方式之间存在细微差别。

    case UP_k: 
    f_zPosition-=CAM_MOVEMENT_SPEED; 
     break; 

case 'W': 也使用相同的代码。

但在游戏中,由于某种原因,方向键的移动比WASD键的移动要流畅得多。

    Win32 如何在内部处理这些事件? 我该怎么做 WASD 的移动就像箭头键一样流畅。

我想知道是否有人在 Win32 中注意到这一点,这是我的事件循环代码,按照 cmets 中的要求。

do

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

while(msg.message != WM_QUIT);

【问题讨论】:

你能发布你的事件循环的代码吗?我的猜测是它与 TranslateMessage 函数有关。 我知道没有区别。当然,除非你有 Alt/Meta/etc。同时按下键;然后箭头键的行为略有不同。但是,如果您按下 Alt,则您必须完全处理不同的窗口消息。 @AndonM.Coleman,我目前没有使用 Alt。它简单的箭头键和 WASD。 jspurim,我已经添加了事件循环。 哦,嗯。你实际上在这里做了一些我建议你避免的事情:在按住键的同时依赖 autorepeat 一遍又一遍地发送WM_KEYDOWN 消息。我会总是忽略lParam 位30 设置为1WM_KEYDOWN 事件,这表示重复的关键消息。当用户按下按钮时,不要处理移动,而是在最初按下键时设置一个状态,然后在释放它时将其重置。当需要定期处理移动时,请检查设置的状态,而不是仅响应自动重复生成的消息。 你试过TranslateMessage 注释掉我建议的吗?有效果吗? 【参考方案1】:

win32 应用程序上的正常消息处理循环调用两个函数:

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

第一个函数TranslateMessage 查看虚拟键消息(例如WM_KEYDOWN 消息),然后如果按下的键对应于字符键,则将字符消息添加到消息队列中。如果您按住'W' 键,由于自动重复功能,会生成很多WM_KEYDOWN。箭头键也是如此,但对于'W',对于每个WM_KEYDOWN,还会生成一个WM_CHAR,您的应用程序必须对其进行处理。

即使您的应用程序只查看 WM_CHAR 并丢弃它们,由此产生的延迟也可能很明显,尤其是因为您每次处理 WM_KEYDOWN 时都会移动相机

更多信息请查看this。

解决办法:

您应该使您的移动速度独立于您处理输入的速度。有两种主要方法可以做到这一点:

固定帧率:每秒更新屏幕固定次数。每次更新时,将相机/播放器移动相同的距离。 定时帧尽可能多地更新屏幕,但要测量迭代之间的时间。移动时,将移动速度乘以自上次更新以来的时间。

在这两种情况下,在处理输入时不要移动你的角色。相反,当你得到 WM_KEYDOWN 时,将布尔值 playerIsMoving 设置为 true,并将其重置为当您收到WM_KEYUP 时,false。然后,仅当变量位于 true 中时,才在生成下一帧时应用移动。

[更新]

看到你的主循环后: 尝试像这样修改它:

修改1:注释掉TranslateMessage。除非您想在应用中包含文本框等,否则您不需要它。

修改 2:添加一个单独的函数来更新世界状态。

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;

【讨论】:

以上是关于WM_KEYDOWN 如何处理不同的键?的主要内容,如果未能解决你的问题,请参考以下文章

Redis 面试宝典之 Redis 如何处理已经过期的数据?

PHP如何处理三重加密的32字节密钥

PHP如何处理三重加密的32字节密钥

如何处理不同的利润?

spring-data-redis multiGet 如何处理缓存未命中?

如何处理不同语言的 iOS 推送通知