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

Posted

技术标签:

【中文标题】我应该使用 DirectInput 还是 Windows 消息循环?【英文标题】:Should I use DirectInput or Windows message loop? 【发布时间】:2011-01-11 00:51:21 【问题描述】:

我正在开发 C++ DirectX 2D 游戏,我需要键盘和鼠标输入。 ***说:

Microsoft 建议新应用程序使用 Windows 消息循环而不是 DirectInput 进行键盘和鼠标输入

那么我应该如何使用它呢? 我有一个 GameScreen 类负责绘图和更新(游戏逻辑),我在 Windows 消息循环中调用 Draw 和 Update 方法。

谢谢

【问题讨论】:

【参考方案1】:

如果游戏只有一个窗口,那么据我所知,区别纯粹是口味问题。但是,如果您拥有(或计划拥有,或不能肯定地排除将来拥有)多个窗口的选项,那么 Windows 消息传递可能会令人厌烦。

问题在于,默认情况下,键盘/鼠标消息仅路由到当前焦点所在的窗口,并且通常在您希望能够切换焦点的游戏中(到高分视图、雷达视图上的敌人或其他)和仍然保持交互性。简单的解决方案是针对每个需要键盘/鼠标输入来直接查询它的模块,而不是依赖消息转发 - 因此是 DirectInput。

当然,我不能对你的具体情况说太多 - 只是我的 2c。

【讨论】:

键盘和鼠标事件不会转到焦点窗口。 由您的消息泵(这就是 DispatchMessage 所做的)将键盘事件发送到焦点窗口。鼠标事件转到光标下的窗口。 对。谢谢!我最初只用键盘评论,然后才将它“固定”到键盘/鼠标..【参考方案2】:

XInput 绝对是要走的路。低延迟输入对游戏至关重要,而 XInput(在新的 DirectXSDK 中替代 DirectInput)正是为这些用例而设计的。

您还可以开箱即用地支持游戏控制器、操纵杆等。

【讨论】:

DirectInput 不会降低键盘和鼠标事件的延迟。但如果您以后想要支持操纵杆等,它确实为您提供了更一致的输入模型。 你说得对,但我的意思是 DirectInput(和 XInput,它是 DInputs 的后继者)的延迟比 Windows 消息低。 但不适用于键盘和鼠标输入。 DirectInput 只是针对键盘和鼠标的 Win32 API 的封装。 这不是真的。您对 DInput 1.0 是正确的,因为这是对 win32 输入的简单包装,但由于 2.0 DInput 直接与驱动程序对话。 Windows 消息系统在将输入数据传递给应用程序(WM_CLICK、WM_DOUBLECLICK 等)之前对输入数据进行预处理,处理所有焦点要求等。这是延迟差异的主要来源。 顺便说一句。您可以在“Direct X 输入 -> 直接输入 -> 了解 DirectInput”中的 DirectX SDK 文档中找到对我声明的引用【参考方案3】:

由于您几乎必须运行消息泵才能拥有一个窗口,因此您也可以使用该泵来处理键盘和鼠标输入。是否将键盘事件交给子窗口完全取决于您的泵,如果您愿意,可以在泵中处理它们。

您的典型消息泵如下所示:

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

对于游戏,您的泵可能看起来更像这样

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,这样即使没有要处理的消息,您也可以定期唤醒,但在有消息时立即唤醒。

【讨论】:

我想知道为什么不应该使用 DirectInput。谢谢 亚历克斯在***.com/questions/2165230/…回答【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

您能否进一步证实“仅查询 Windows 的原始输入界面”(也许有参考)?【参考方案6】:

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

while (GetMessage(&msg, NULL, 0, 0))

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

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

【讨论】:

【参考方案7】:

特殊情况下只能使用directinput

与 getmessage 一起使用的 Windows 消息循环的好处是它使用 0% 的 cpu 使用率。如果您正在执行非处理器密集型操作,例如等待来自用户的密钥、从用户那里收集数据(例如在数据库或税务程序中,甚至是文字处理器),那么将 Windows 消息与 getmessage 一起使用是有意义的。我上面所有的程序日期都是用户按键的过程。

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

特殊情况-使用直接输入

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

如果以上任何一个为真,则使用直接输入。

如果您只是从用户那里收集数据,那么您将需要使用 sleep 命令来暂停程序的执行。如果程序只是坐在那里等待用户的密钥,您希望程序在任务管理器中的 cpu 使用率为 0%。

使用睡眠功能使程序进入睡眠状态,直到您想轮询直接输入的键。

因此,如果您正在执行从用户那里收集密钥的简单普通任务,如您所见,直接输入只会浪费编程时间。

【讨论】:

以上是关于我应该使用 DirectInput 还是 Windows 消息循环?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我无法使用 DirectInput 获取键盘?

使用 DirectInput 在 C# 中检索键盘状态?

如何使用 DirectInput 在我的游戏中创建聊天框?

如何使用 directInput 发送释放键? [关闭]

C# DirectInput - 确定游戏手柄控制器类型

在 Windows 10 上使用 DirectInput 和 XBOX One 控制器和窗口焦点