【问题标题】:Simulate lasting key press模拟持续按键
【发布时间】:2020-02-08 13:45:46
【问题描述】:

在我的程序中,我想模拟按键。例如我想说 Press(KeyA, 1000),其中第一个参数是 KeyCode,第二个参数是以毫秒为单位的时间。 KeyA 等是 typedef,这些方法非常适合只需按下按钮一次。

void PressKey(INPUT &ip, int keycode, int ms)
{
    // Press the  key
    ip.ki.wVk = keycode; // virtual-key code for the key
    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));

    sleep(ms);

    // Release the  key
    ip.ki.dwFlags = KEYEVENTF_KEYUP; // KEYEVENTF_KEYUP for key release
    SendInput(1, &ip, sizeof(INPUT));
}

int main()
{


    // input event
    INPUT ip;
    INPUT ipMouse;

    ip.type = INPUT_KEYBOARD;
    ip.ki.wScan = 0; // hardware scan code for key
    ip.ki.time = 0;
    ip.ki.dwExtraInfo = 0;



    PressKey(ip, KeyM, 1000);


我认为按下键,然后等到释放它会算作按住按钮按下,但事实并非如此。此外,我不想只说 PressKey(ip, KeyM) 一千次以获得与按住按钮 5 秒相同的输入。现在有人如何让程序模拟更长的输入吗?

【问题讨论】:

  • 按键的目标是什么?是您自己的程序还是其他应用程序?它怎么不工作?你 sleep() 的事实会阻止你自己的主循环在那段时间运行。如果它是另一个应用程序,它可能会检查键盘扫描码或读取输入。
  • 我自己写了一个主机游戏,我想用不同的 /O 选项编译,然后在 excel 中分析优化。我现在已经完成了所有设置,在 csv 文件中输出 fps 和 cpu 等效果很好。不过,要真正比较优化,我总是需要相同的输入。这就是为什么我需要按住“w”键一段固定的时间,因为我的游戏的移动速度取决于帧之间的时间差。这是有道理的,还是我只是想错了方向。我希望输入是外部程序,所以没有对运动进行硬编码
  • 不记录输入。而是记录游戏引擎的状态转换,然后重播这些。
  • 录制输入完全没问题(为多个 AAA 游戏做到了)。从主循环重放它是一个好方法。但是,使用 win32 输入注入并不是正确的方法。
  • X-Y 问题:正确的问题是:如何保存输入并重放它们?如何配置我的游戏?以及如何保持游戏逻辑的确定性?

标签: c++ winapi keystroke


【解决方案1】:

我将回答更一般的问题“如何使用特定输入重播我的游戏运行?”

重放输入、分析、检查确定性或一般 QA 的技巧是将两个元素与游戏处理分开:

  • 对计时器的任何访问
  • 任何读取玩家输入的内容

如果您的游戏编写得很好(没有未定义的行为),具有给定的一组输入和相关的时间,它的行为应该始终相同。相同的输入同时产生相同的输出。

因此,将 TimeGetTime()、std::clock、rdtsc 或您用来读取时间的任何内容包装到您自己的 getTime() 函数中。同样,将 win32 输入读取和/或扫描码读取包装到您自己的 getInputs() 中。

运行游戏,但除了使用它们之外,还将所有输入和时间戳保存到一个文件中。 Log.txt 什么的。理想情况下,您的游戏应该每帧只询问一次时间,但是,嘿,我见过每帧查询当前时间数千次的游戏。

现在,修改您的游戏,以便从日志文件中获取值,而不是获取真实的输入或真实的挂钟。如果您的游戏是确定性的,那么您现在应该能够重播相同的事件序列。

然后您可以对其进行概要分析。请注意,由于时间是从文件中读取的,因此如果游戏在重玩时碰巧变慢了,这不是问题。你的角色会跳同样的长度,落在同一个地方,等等......它看起来会更慢。这使您可以使用最密集的分析设置,即使游戏运行速度慢了 10 倍,它仍然可以执行完全相同的操作,并且您可以有效地衡量花费的时间。

它还为您准备好设置,让 QA 可以保存回放并且您可以进行调试。

记录所有这些需要更多的工作,并且需要小心不要有任何不确定性。

但是,另一方面,如果您按照最初的建议进行,输入注入将不会完全同时发生。你失去了一些 QA repro 能力。从运行到运行,行为会有所不同。

分析器开销也会影响计时,有时会导致测量无效。

所以,我的建议:

  • 制作一个接近确定性的游戏(很可能总会有细微的差异)
  • 将所有输入(包括时间)包装在一个记录函数后面
  • 全部保存以记录
  • 将它们替换为记录的历史记录以重播

要具体解决问题的框架: 游戏不是通过 win32 系统提供输入,而是调用您自己的 getInput(),并且在重玩时获取存储的输入,从而绕过整个 win32 系统。

【讨论】:

    【解决方案2】:

    但是没有这样的 API 来模拟按键。

    当您测试一个窗口应用程序时,当您按住键盘上的某个键时,它会收到连续的WM_KEYDOWN 消息。

    如果您的应用程序是窗口应用程序而不是控制台应用程序,您可以检测到low level keyboard hook中的第一个WM_KEYDOWN并启动一个5秒的计时器,当计时器超时时,发送WM_KEYUP消息,所以模拟按住的新闻。您只能拨打SendInput一次,发送一键消息。

    对于不能被 hook 的控制台应用程序,在指定时间内重复调用 SendInput 是一个解决方案。对此有任何顾虑吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-29
      • 1970-01-01
      • 2012-01-31
      • 2013-07-24
      • 2020-04-29
      • 2011-03-04
      • 2010-11-22
      • 2011-11-10
      相关资源
      最近更新 更多