SFML 2.0中的按键重复问题

4
有没有一种方法可以在SFML中接收到刚刚按下(而不是长按)的按键,或者模仿这种效果?
我在这里轮询我的事件,
while (window.pollEvent(event));

然后在循环中稍后,虽然超出范围但使用相同的事件,我读取:

if (event.type == sf::Event::KeyPressed)
{
    if (event.key.code == sf::Keyboard::Up)
    {
        decision--;
        if (decision < 0)
        decision = CHARACTER_MAX - 1;
    }
}

当按下向上箭头时,这会使“decision”持续减少。使用“sf::event”,我是否可以在第一次按下它而不是按住它时读取它?
另外,如果不可能或者您不熟悉SFML,有没有什么方法可以用很少或没有全局变量来模拟这样的情况?(例如“is_already_held”),例如https://gamedev.stackexchange.com/questions/30826/action-button-only-true-once-per-press 我意识到这可能是唯一的方法(在更广泛的范围内声明一个变量),但我更喜欢不这样做,因为我不断退出作用域,并尽可能避免使用全局变量。

在执行 while (window.pollEvent(event)); 代码时,请注意结尾的分号;,因为只有在这样做后,你才能获得最后一个事件。 - teh internets is made of catz
3个回答

8

您可以使用sf::Window::setKeyRepeatEnabled禁用键重复:

window.setKeyRepeatEnabled(false);

在创建窗口后只需调用一次(就足够了)。

文档在这里


如果由于任何原因您不想使用此功能,则可以有一个布尔表来存储每个键的状态。以下是伪代码,展示如何保持表格最新并集成您的“决策”代码。
// Init the table of state. true means down, false means up.
std::array<bool, sf::Keyboard::KeyCount> keyState;
keyState.fill(true);

// :
// :

// In the event loop:
if (event.type == sf::Event::KeyPressed)
{
    // If the key is UP and was not previously pressed:
    if (event.key.code == sf::Keyboard::Up && !keyState[event.key.code])
    {
        decision--;
        if (decision < 0)
            decision = CHARACTER_MAX - 1;
    }

    // Here comes the rest of your code and at the end of the if-body:

    // Update state of current key:
    keyState[event.key.code] = true;
}
else if (event.type == sf::Event::KeyReleased)
{
    // Update state of current key:
    keyState[event.key.code] = false;
}

第三种选择是使用第三方库,例如Thor。它仍在开发中,因此您可以预期作者会进行一些更改。这里有一个关于Action的教程,也就是您需要的内容。

2
你可能想要使用sf :: Event :: KeyReleased 代替sf :: Event :: KeyPressed 事件,后者会在按键被“按住”时继续运行,而这是您的计算机比人手快得多的触发方式。
如果我运行:
while(window.pollEvent(event)) {
   switch (event.type) {
     case sf::Event::KeyPressed:
       if(event.key.code == sf::Keyboard::Enter) {
         std::cout << "Enter key pressed!" << std::endl;
       }
     break;
   default:
     break;
   }
}

I'll get:

Enter key pressed!
Enter key pressed!
Enter key pressed!
Enter key pressed!
... etc

根据我按住回车键的时间长短,可以任意次数(14、23、9、31等)运行。

如果我运行:

Original Answer

翻译成"最初的回答"
while(window.pollEvent(event)) {
  switch (event.type) {
    case sf::Event::KeyReleased:
      if(event.key.code == sf::Keyboard::Enter) {
        std::cout << "Enter key pressed!" << std::endl;
      }
      break;
    default:
      break;
  }
}

I'll get:

Enter key pressed!

只有按下回车键一次才会触发。 :)

有时候你需要使用sf::Event::KeyPressed事件,比如在枪械游戏中按住“缩放”按钮来使用瞄准镜,或在文件编辑器中按住“Ctrl”键来使用“Ctrl+Alt+键”的组合键等。

注:KeyPressed事件是指当按下任意键时触发,而KeyReleased事件是指当释放按键时触发。


注意:我之所以更喜欢这种方法而不是setKeyRepeatEnabled()或任何“按键按下”事件,是因为几乎总是决定按键按下是毫无逻辑的。如果您需要单击触发事件而不是按住按键,则首先不会按住按键。这种方法干净利落,仍然可以在您仅简单地点击按键时让您使用“按键按住”事件的全部功能,而在大多数情况下,如果您不仅仅是轻敲键盘,就不会使用它。 - Rebel Rae
有一些合法的用例,可能需要同时拥有两者。例如,如果按住一个键会触发一种行为,而快速按下并反复释放则会触发其他行为。 - SasQ

1
为了避免按键时重复触发事件,您可以使用sf::Event::KeyReleased而不是sf::Event::KeyPressed。我假设这是您的目标。
如果不是,您将需要存储按钮状态。类是合理的答案,因为它将状态与行为结合在一起。

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