【问题标题】:SDL2 going too fast -- why does it read multiple inputs when I press just one button?SDL2 速度太快——为什么我只按一个按钮时它会读取多个输入?
【发布时间】:2016-02-13 21:42:44
【问题描述】:

我已经开始编写一个 SDL2 程序。我希望整数 count 在用户按下右箭头键时上升一位,当用户按下左箭头键时下降一位。

#include <iostream>
#include <SDL2/SDL.h>

int main(){
    SDL_Window *window= SDL_CreateWindow("test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);
    int count=      0;
    bool isRunning= true;
    SDL_Event ev;
    while(isRunning){
        if(SDL_PollEvent(&ev)){
            if(ev.type == SDL_QUIT  ||  ev.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
                return 0;
        }

        const Uint8 *keystate=    SDL_GetKeyboardState(NULL);

        if(keystate[SDL_SCANCODE_LEFT])
            --count;
        else if(keystate[SDL_SCANCODE_RIGHT])
            ++count;

        std::cout << count << std::endl;
    }

    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}

以下是我启动程序并简要点击右侧时打印的示例——全部在一两秒内完成:

0
0
0
0
0
0
0
0
0
0
0
1
2
3
4
4
4
4
4

当我快速点击右箭头键时,我希望 count 仅上升一位,但它却从 0 变为 4

为什么?

我该如何解决这个问题?

【问题讨论】:

    标签: c++ sdl sdl-2


    【解决方案1】:

    您的问题是您向 SDL 请求密钥状态数组,在您的场景中,这不是最好的方法。那么,SDL 在这种情况下会做什么呢?它只是为您提供一个包含信息的数组,无论当前键是否被持有。您尝试尽可能短地按下按钮,但您的循环非常及时。因此,要解决此问题,您可以使用事件系统的 keydown 功能,当您按下按钮时,它会为您提供 true (其对是 SDL_KEYUP 用于按键释放事件)。

    主要区别在于问题:是按键被按住,还是我只是按下并改变了它的状态?

    所以,这里有一个例子(在 SDL_PumpEvent 或 SDL_PollEvent 中使用):

    //...
    if (event.type == SDL_KEYDOWN)
    {
        if (event.key.keysym.sym == SDLK_LEFT)
        //do left key things...;
        else if (event.key.keysym.sym == SDLK_RIGHT)
        //do right key stuff...;
    }
    //...
    

    请注意,此方法不使用扫描码,而是使用键码。 (由于键盘类型不同,它们可以产生与扫描码不同的结果)。 SDLK_ 常量都是键码。此外(如果您考虑游戏),扫描码有利于玩家移动,keydown 事件有利于 GUI 元素

    更多信息参考:https://www.libsdl.org/release/SDL-1.2.15/docs/html/guideinputkeyboard.html

    希望你能理解! 拉索洛兹

    【讨论】:

      【解决方案2】:

      类似于this Question(它实际上询问鼠标按钮,但它与键盘相同):

      不要使用keyboardState,而是使用SDL 事件。 SDL 每次按下按钮都会触发一个事件,而快速的while-loop 可以在单次按键期间触发多次。

      【讨论】:

        【解决方案3】:

        尽管该示例远不是一个最小、完整的示例并且无法编译,但我想问题可能是由于缺少对 SDL_PumpEvents 的调用。

        来自documentation

        注意:使用 SDL_PumpEvents() 更新状态数组。

        否则,状态数组将不会更新为您正在体验的结果。

        注意

        也就是说,尝试依赖事件而不是用于表示键盘的内部状态数组。

        编辑

        问题更新后更新。

        您应该将SDL_PollEvent 上的if 替换为while,如下所示:

        while (SDL_PollEvent(&event)) {
            // here you have an event, you can use it
        }
        

        否则,即使没有事件,它也会跳过if并遍历其他语句。
        这意味着如果没有事件,键盘的状态不会在第一次按键后更新,但您仍然会对其进行迭代。

        有关SDL_PollEvent 工作原理的更多详细信息,请参阅here

        【讨论】:

          【解决方案4】:

          我将while 循环更改为:

          while(isRunning){
              while(SDL_PollEvent(&ev)){
                  if(ev.type == SDL_QUIT  ||  ev.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
                      return 0;
                  else if(ev.type == SDL_KEYDOWN){
                      if(ev.key.keysym.scancode == SDL_SCANCODE_LEFT){
                          --count;
                          std::cout << count << std::endl;
                      }
                      else if(ev.key.keysym.scancode == SDL_SCANCODE_RIGHT){
                          ++count;
                          std::cout << count << std::endl;
                      }
                  }
              }
          }
          

          如果我理解正确,SDL_PollEvent 只会在对象ev 的地址为真时触发。如果用户按下一个键,它会继续。如果键是向左箭头,count 向下一。如果key是右箭头,count加一。

          现在cout 以我希望的方式打印。

          按几次右后输出:

          0
          1
          2
          3
          4
          

          然后离开:

          4
          3
          2
          1
          0
          

          【讨论】:

          • 如果您还有其他问题,请创建一个新问题。如果这是一个谢谢的答案,请接受其中一个答案(正确的答案)并删除那个答案。谢谢。
          猜你喜欢
          • 1970-01-01
          • 2019-11-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-23
          相关资源
          最近更新 更多