好吧,我正在努力避免使用已弃用的DirectInput。
但我需要在游戏的每个“框架”或“迭代”中抢夺所有关键状态,以便我可以采取相应的行动。例如,如果玩家在VK_RIGHT键上按下,那么他将只在该帧上移动一个smidgen。
WM_INPUT消息的问题是,由于游戏循环的编写方式,它们每帧可能出现不可预测的次数:
MSG message ; while( 1 ) { if( PeekMessage( &message, NULL, 0, 0, PM_REMOVE ) ) { if( message.message == WM_QUIT ) { break ; // bail when WM_QUIT } TranslateMessage( &message ) ; DispatchMessage( &message ) ; } else { // No messages, so run the game. Update() ; Draw() ; } }
因此,如果多个 WM_INPUT消息堆叠在那里,那么它们将在Update()/ Draw()之前得到处理。
我通过使用BOOL数组来解决这个问题,以便记住哪些键已关闭:
bool array_of_keys_that_are_down[ 256 ] ; case WM_INPUT : if( its keyboard input ) { array_of_keys_that_are_down[ VK_CODE ] = TRUE ; }
这很好,因为Update()函数检查
void Update() { if( array_of_keys_that_are_down[ VK_RIGHT ] ) { // Move the player right a bit } }
但现在问题是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 ) ) ;
我一直在努力解决这个问题,而一个解决方案只会重置一个关键状态,一旦被释放
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 消息。这会产生奇怪的“粘滞键”错误。
答案 0 :(得分:6)
您可以改用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(¤t[0]); // normally do error checking
你已经完成了。
答案 1 :(得分:1)
所提出的解决方案是正确的方法 - 忽略自动重复,只记录向下/向上状态。
要处理任务切换问题,请查看WM_ACTIVATE
消息 - 它可以检测窗口何时失去焦点。当相关窗口发生这种情况时,假设所有键都被释放。 (这与使用非独立合作级别时使用DirectInput时的情况类似。)
答案 2 :(得分:0)
正如你所说的有延迟,我认为你想要减少延迟,为什么不调用'SystemParametersInfo'设置打字延迟和速度,你需要看一下键盘延迟..'SPI_GETKEYBOARDDELAY'和键盘速度'SPI_GETKEYBOARDSPEED'。该功能如下所示:
int SetKeyboardSpeed(int nDelay){ /* fastest nDelay = 31, slowest nDelay = 0 */ return (SystemParametersInfo(SPI_SETKEYBOARDSPEED, nDelay, NULL, SPIF_SENDCHANGE) > 0); } int SetKeyboardDelay(int nDelay){ /* 0 = shortest (approx 250ms) to 3 longest (approx 1sec) */ return (SystemParametersInfo(SPI_SETKEYBOARDDELAY, nDelay, NULL, SPIIF_SENDCHANGE) > 0); }
修改:回应 Blindy 对downvote的评论 - 你100%正确 - 从未意识到我输入的内容是代码进入这个...是的,你已经把手指放在它上面,永远不要在没有用户知道的情况下改变全局/系统范围的设置!我的立场由Blindy的评论纠正。请忽略我的答案,因为它是100%错误!
希望这有帮助, 最好的祝福, 汤姆。
答案 3 :(得分:0)
这种组合应该保持一致。
答案 4 :(得分:0)
解决问题的最佳(就结果的一致性和效率而言)解决方案是使用原始输入。基于事件,快速,高效,不会丢失输入。
您可能会错过 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 消息。它使 奇怪的“粘滞键”错误。
当您 RAWINPUTDEVICE structure 时,通过 register your raw input device 中的 RIDEV_INPUTSINK 标志修复了此问题:即使您的窗口不在前台,您也会收到消息。
您的问题似乎不在您使用的 API 中。您想要一种与系统交互的特定方式:
<块引用>但我需要在游戏的每个“帧”或“迭代”中抢夺所有 关键状态,以便我可以采取相应的行动。例如,如果玩家按下 VK_RIGHT 键,那么他将在该帧上向右移动一点点。
WM_INPUT 消息的问题在于它们可能会出现不可预测的 每帧的次数,因为游戏循环的方式 写的。
我通过使用一组 BOOL 来记住哪些键来解决这个问题 下来了。
您想在检查时知道关键状态,因此请编写您自己的输入处理层。您想要基于轮询的系统,因此创建 KeyboardStateHandler 类,使其响应所有按键按下和按键释放原始输入事件,然后在您的游戏循环中,您调用 keyboardStateHandler.GetKeys() 并获取所有按键的状态。< /p>
我认为您真正想做的是创建一个合适的强大输入处理层,这将是您在上面提出的所有问题的解决方案。
这里只是一些:
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 或其他方法,您都希望将输入处理与游戏逻辑分开。