GetRawInputData 与 GetAsyncKeyState()

Posted

技术标签:

【中文标题】GetRawInputData 与 GetAsyncKeyState()【英文标题】:GetRawInputData vs GetAsyncKeyState() 【发布时间】:2010-02-14 16:44:32 【问题描述】:

好吧,我试图避免使用已弃用的 DirectInput。

但我需要在游戏的每个“帧”或“迭代”中获取所有关键状态,以便我可以采取相应的行动。例如,如果玩家按下 VK_RIGHT 键,那么他将在该帧上向右移动一点点。

WM_INPUT 消息的问题在于,由于游戏循环的编写方式,它们每帧出现的次数无法预测:

味精消息; 而(一) if( PeekMessage( &message, NULL, 0, 0, PM_REMOVE ) ) 如果(message.message == WM_QUIT) 休息 ; // 当 WM_QUIT 时保释 TranslateMessage( &message ) ; DispatchMessage( &message ) ; 别的 // 没有消息,所以运行游戏。 更新() ; 画() ;

因此,如果多个 WM_INPUT 消息堆叠在那里,那么它们都将在Update()/Draw() 之前得到处理。

我通过使用 BOOL 数组来记住哪些键被按下来解决了这个问题:

bool array_of_keys_that_are_down[256]; 案例 WM_INPUT : if(它的键盘输入) array_of_keys_that_are_down[VK_CODE] = TRUE;

这很好用,因为 Update() 函数会检查

无效更新() 如果(array_of_keys_that_are_down[VK_RIGHT]) // 将玩家向右移动一点

但现在的问题是,WM_INPUT 消息生成的经常不够。在第一次按下 VK_RIGHT 和随后的 VK_RIGHT 消息之间有大约 1 秒的延迟,即使玩家一直将手指放在它上面。它不像 DirectInput,您可以在其中 keyboard->GetDeviceState( 256, (void*)array_of_keys_that_are_down );(通过一次调用获取每帧的所有关键状态)

所以我迷路了。除了对我需要监控的每个键使用 GetAsyncKeystate() 函数调用之外,如果您不能可靠地抓取每帧的所有键状态,我认为没有办法避免使用 DirectInput。

在我看来,DirectInput 是一个非常好的解决方案这个问题,但如果它被弃用,那么真的必须有某种方法可以方便地仅使用 Win32 api 来做到这一点。

目前array_of_keys_that_are_down 被重置为每帧的所有 FALSE。

memset(array_of_keys_that_are_down,0,sizeof(array_of_keys_that_are_down));

*编辑

我一直在解决这个问题,一个解决方案是只重置一个关键状态,一旦它被释放

案例 WM_INPUT : if(它的键盘输入) 如果(它是按下) array_of_keys_that_are_down[VK_CODE] = TRUE; 别的 array_of_keys_that_are_down[VK_CODE] = FALSE;

我不喜欢这个解决方案,因为它看起来脆弱。如果用户在按下键时从应用程序中切换,那么该键将被“卡住”,直到他切换回来并再次按下相同的键,因为我们永远不会收到上击 WM_INPUT 消息。它会导致奇怪的“粘滞键”错误。

【问题讨论】:

为什么不直接捕获 WM_KEYDOWN 或 WM_KEYUP 消息? 嗯,同样的原因,真的 - 有一个延迟.. 【参考方案1】:

您可以改用GetKeyboardState。您通常想要的是两个数组;一个存储前一帧的输入状态,一个存储当前帧。这允许区分被持有和被触发。

// note, cannot use bool because of specialization
std::vector<unsigned char> previous(256);
std::vector<unsigned char> current(256);

// in update_keys or similar:
current.swap(previous); // constant time, yay
GetKeyboardState(&current[0]); // normally do error checking

你已经完成了。

【讨论】:

太棒了!我错过了这个功能。所以,一个好奇心——我一直听到“directInput 真的使用 RawInput 函数”——这和 DirectInput 一样快吗,当你使用 GetDeviceState() 时,DirectInput 是否真的最终使用了这个函数? @bobobobo:老实说,我不能告诉你性能比较。我已经使用了所有三个(this、GetAsyncKeyState 和 DirectInput),但从未针对它们进行过分析。输入花费的时间很短,可能不用担心。也就是说,我猜 DirectInput 做了一些与此非常相似的事情(如果我记得的话,它主要委托给 Win32)。这可能比GetAsyncKeyState 更快,尽管我怀疑GetAKS 实际上可能只是查看GetKeyboardState 为我们复制的数组中的某个索引。也就是说,使用GetAKS,我们会循环获取状态, GeytKS 将把该外观移动到memcpy。所以这应该比GetAKS 稍微快一点。不过,这都是猜测;要真正知道您必须进行概要分析。【参考方案2】:

提出的解决方案是正确的方法——忽略自动重复,只记录向下/向上状态。

要处理任务切换问题,请查看WM_ACTIVATE 消息——它可以检测窗口何时失去焦点。当相关窗口发生这种情况时,假设所有键都被释放。 (这类似于在使用非排他合作级别时使用 DirectInput 所做的事情。)

【讨论】:

【参考方案3】:

正如你所说的有延迟,我的印象是你想减少延迟,为什么不调用'SystemParametersInfo'来设置打字延迟和速度,你需要看看键盘延迟..“SPI_GETKEYBOARDDELAY”和键盘速度“SPI_GETKEYBOARDSPEED”。函数如下所示:

int SetKeyboardSpeed(int nDelay) /* 最快的 nDelay = 31,最慢的 nDelay = 0 */ 返回(系统参数信息(SPI_SETKEYBOARDSPEED,nDelay,NULL,SPIF_SENDCHANGE)> 0); int SetKeyboardDelay(int nDelay) /* 0 = 最短(大约 250 毫秒)到 3 最长(大约 1 秒)*/ 返回(系统参数信息(SPI_SETKEYBOARDDELAY,nDelay,NULL,SPIIF_SENDCHANGE)> 0);

编辑: 回应 Blindy 的反对意见 - 您 100% 正确 - 在我输入将代码写入此...是的,您已经对此有所了解,永远不要在用户不知道的情况下更改全局/系统范围的设置!我接受 Blindy 的评论的纠正。请忽略我的回答,因为它是 100%错误

希望这会有所帮助, 最好的祝福, 汤姆。

【讨论】:

-1,您的应用不拥有计算机,它不能只更改用户的系​​统设置。 @Blindy - 请看我修改后的答案!感谢您的提醒! :) +1 表示您对我的评论...这实际上抵消了!!! :) 大声笑【参考方案4】:
    使用窗口消息来检测 按键,并跟踪数组 自己按键。 注意 WM_ACTIVATE 消息并使用 那时的 GetAsyncKeyboardState “修复”数组以匹配 实际状态。

这种组合应该保持一致。

【讨论】:

【参考方案5】:

解决问题的最佳方法(就结果的一致性和效率而言)是使用原始输入。基于事件,快速,高效,不会丢失输入。

您可能会错过 GetAsyncKeyState 调用的输入。例如,假设在以 60 Hz 运行的游戏迭代开始时,您调用 GetAsyncKeyState 并且未按下键。到现在为止还挺好。然后 5 毫秒后你按下一个键,比如说 VK_TAB,并按住它 5 毫秒。然后在下一次游戏迭代开始时(大约 6.67 毫秒后)再次调用 GetAsyncKeyState。但到那时,该键不再被按下。从游戏的角度来看,它从未被按下!它可能看起来影响深远,但事实并非如此。我玩过使用这个系统的游戏,但以 60 FPS 的速度错过了输入。令人沮丧和不必要的。

我一直在解决这个问题,一个解决方案是只重置一个 关键状态,一旦被释放

case WM_INPUT :
    if( its keyboard input )
    
        if( its a down press )
            array_of_keys_that_are_down[ VK_CODE ] = TRUE ;
        else
            array_of_keys_that_are_down[ VK_CODE ] = FALSE ;
    

我不喜欢这个解决方案,因为它看起来很脆弱。如果用户 按下键时从应用程序切换,然后该键 将被“卡住”,直到他切换回来并再次按下相同的键 因为我们永远不会收到上行 WM_INPUT 消息。它使 奇怪的“粘滞键”错误。

当您 register your raw input device 时,RAWINPUTDEVICE structure 中的 RIDEV_INPUTSINK 标志已解决此问题:即使您的窗口不在前台,您也会收到消息。

您的问题似乎不在您使用的 API 中。您想要一种与系统交互的特定方式:

但我需要在游戏的每个“帧”或“迭代”中抢夺所有 关键状态,以便我可以采取相应的行动。例如,如果玩家按下 VK_RIGHT 键,那么他将在该帧上向右移动一点点。

WM_INPUT 消息的问题是它们可能会出现不可预测的情况 每帧的次数,因为游戏循环的方式 写的。

我通过使用 BOOL 数组来记住哪些键来解决此问题 下来了。

您想在检查关键状态时知道它们,所以编写您自己的输入处理层。您需要基于轮询的系统,因此创建 KeyboardStateHandler 类,使其响应所有按键和按键释放原始输入事件,然后在您的游戏循环中,调用 keyboardStateHandler.GetKeys() 并获取所有按键的状态。

我认为您真正想要做的是创建一个适当的健壮输入处理层,这将解决您在上面提出的所有问题。

这里只是一些:

https://bell0bytes.eu/inputsystem/Web archive link

https://www.gamedev.net/blog/355/entry-2250186-designing-a-robust-input-handling-system-for-games/Web archive link

https://blog.gemserk.com/2012/08/23/decoupling-game-logic-from-input-handling-logic/Web archive link

无论您使用什么来实际获取输入:GetKeyAsyncState、DirectInput、Raw Input 或其他方法,您都希望将输入处理与游戏逻辑分开。

【讨论】:

以上是关于GetRawInputData 与 GetAsyncKeyState()的主要内容,如果未能解决你的问题,请参考以下文章

LDAP 与 MYSQL .. JA-SIG CAS 与 LDAP 与 CAS 与 MySQL

python网络编程基础(线程与进程并行与并发同步与异步)

=与==&与&&| 与 || 的区别

与 0 进行比较与与某个值进行比较是不是更快?

三.工具与市场-债券与债务股票与公司

RESTfulREST 与 RESTful 理解与实践