我应该使用DirectInput还是Windows消息循环?

16

我正在开发一个C++的DirectX 2D游戏,需要使用键盘和鼠标输入。
维基百科上说:

微软建议新应用程序使用Windows消息循环处理键盘和鼠标输入,而不是DirectInput

那么我该怎么用呢?
我有一个GameScreen类来处理绘图和更新(游戏逻辑),我在Windows消息循环中调用Draw和Update方法。

谢谢

7个回答

14

由于必须运行消息泵才能拥有窗口,因此您可以使用该泵来处理键盘和鼠标输入。如何处理键盘事件取决于您的消息泵,您可以将其传递给子窗口,或者如果愿意,也可以在消息泵中处理它们。

您典型的消息泵看起来像这样:

while (GetMessage(&msg, NULL, 0, 0))
{
    if (WM_QUIT == msg.message)
       break;
    TranslateMessage(&msg); // post a WM_CHAR message if this is a WM_KEYDOWN
    DispatchMessage(&msg);  // this sends messages to the msg.hwnd
}

对于一个游戏,你的Pump可能看起来更像这样:

while (true)
{
   if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
   {
      bool fHandled = false;
      if (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)
         fHandled = MyHandleMouseEvent(&msg);
      else if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)
         fHandled = MyHandleKeyEvent(&msg);
      else if (WM_QUIT == msg.message)
         break;

      if ( ! fHandled)
      {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
      }
   }
   else
   {
       // if there are no more messages to handle right now, do some
       // game slice processing.
       //
   }
}
当然,你实际的泵可能比这个更复杂,可能还有一个MsgWaitForMultipleObjects,这样即使没有消息要处理,你也可以周期性地唤醒,但是当有消息时应立即处理。

5
为什么不应该使用DirectInput? - Adir
1
由Alex在https://dev59.com/1XI95IYBdhLWcg3wxA09#2168067中回答。 - bobobobo

7

DirectInput已被弃用,原因充分。据我所知,它会创建一个额外的线程并且只是查询Windows的Raw Input接口。为了获得最佳性能,我建议直接使用Raw Input。

如果性能对您不是问题(我猜这是当前硬件上2D游戏的情况),请遵循Microsoft的建议并使用窗口消息,如John Knoeller所述。


2
你能否进一步证实“只查询Windows的原始输入接口”这一点(或许提供一个参考)? - bobobobo

3

参考信息:关于John Knoeller的回答... 一个简单的消息泵看起来像这样:

while (GetMessage(&msg, NULL, 0, 0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

他包含的WM_QUIT测试永远不可能为真,因为当收到WM_QUIT时,GetMessage返回零。然而,在PeekMessage循环中测试WM_QUIT是必需的,因为PeekMessage返回true/false,无论是否返回消息。由于GetMessage阻塞直到返回消息,所以它可以有不同的返回值。

1
如果游戏只有一个窗口,那么我认为区别纯粹是品味问题。但是,如果你有(或计划拥有,或未来不能排除拥有)多个窗口,那么Windows消息可能会让人感到疲劳。
问题在于,默认情况下,键盘/鼠标消息仅路由到当前焦点窗口,并且通常在游戏中,您希望能够切换焦点(到高分视图、雷达视图上的敌人或其他内容),并仍然保持交互性。简单的解决方案是,每个需要键盘/鼠标输入的模块直接查询它,而不依赖于消息转发 - 因此,使用DirectInput。
当然,我无法对您的具体情况做出太多评论 - 这只是我的个人意见。

2
键盘和鼠标事件并不一定会发送到焦点窗口。键盘事件是由消息泵(也就是 DispatchMessage 所做的事情)发送到焦点窗口的。而鼠标事件则会被发送到鼠标下方的窗口。 - John Knoeller
好的。谢谢!我最初只用键盘进行了评论,然后才“修复”为键盘/鼠标。 - Ofek Shilon

1

是的,MSDN的帖子是正确的。使用Windows消息,您可以使用多语言支持(用户可能使用任何类型的键盘)/用户的个人设置(鼠标右键而不是左键)等,这些都必须舍弃以使用DirectInput / XInput。 只需使用这两个来支持游戏手柄/操纵杆。对于其余部分,请使用Windows消息。

关于细节,我同意John Knoeller的答案。


0

仅在特殊情况下使用DirectInput

使用getmessage的Windows消息循环非常好,因为它使用0%的CPU占用率。如果您正在进行不需要大量处理器的操作,例如等待用户的按键、从用户收集数据(例如数据库或税务程序)甚至是文字处理器等,那么使用Windows消息和getmessage就是明智的选择。在上述所有程序中,当用户按下键时,日期就成为了进程。

Windows消息循环处理按键所需的全部内容就是将该程序切换到。鼠标甚至不需要在窗口内。

特殊情况-使用DirectInput

如果您需要: 1)知道何时按下和释放键。您可能也不希望检测到重复的按键作为按键。 2)在切换到另一个程序时在后台处理键。

如果上述条件之一成立,则使用DirectInput。

如果您只是从用户收集数据,则需要使用“休眠”命令暂停程序的执行。如果程序只是在那里等待用户按键,则希望在任务管理器中将程序的CPU使用率降至0%。

使用睡眠函数将程序置于休眠状态,直到您想要轮询直接输入的按键。
因此,如果您只是从用户那里收集按键,那么直接输入只会浪费编程时间。

-2

XInput绝对是正确的选择。低延迟输入对于游戏至关重要,而XInput(在新的DirectX SDK中替代DirectInput)正是为这些用例而设计的。

此外,您还可以直接使用游戏控制器、操纵杆等设备。


1
DirectInput对于键盘和鼠标事件并不具有更低的延迟。但是,如果您以后想要支持游戏手柄等设备,它确实为您提供了更一致的输入模型。 - John Knoeller
1
你说得完全正确,但我的意思是DirectInput(以及DInput的继任者XInput)比Windows消息具有更低的延迟。 - DarthCoder
1
但不适用于键盘和鼠标输入。DirectInput只是键盘和鼠标Win32 API的包装器。 - John Knoeller
这不是真的。对于DInput 1.0,你是正确的,因为它只是win32输入的简单包装器,但自从2.0以后,DInput直接与驱动程序通信。 Windows消息系统在将输入数据传递给应用程序之前对其进行预处理(WM_CLICK、WM_DOUBLECLICK等),处理所有焦点要求等。 这就是延迟差异的主要原因所在。 - DarthCoder
顺便提一下,你可以在DirectX SDK文档中的“Direct X Input -> Direct Input -> Understanding DirectInput”中找到我所说的内容的引用。 - DarthCoder
XInput不仅适用于控制器,提问者询问鼠标和键盘输入。我查看了MSDN文档和DirectX文档,只提到了Xbox控制器。 - Martin Berger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接