SDL2 运行得太快 -- 为什么当我只按一个按钮时它会读取多个输入

SDL2 going too fast -- why does it read multiple inputs when I press just one button?

本文关键字:按钮 一个 输入 读取 运行 为什么 SDL2      更新时间:2023-10-16

我已经开始编写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

为什么?

如何解决此问题?

您的问题是,您要求 SDL 提供密钥状态数组,这在您的方案中不是最佳方法。那么,在这种情况下,SDL 会怎么做呢?它只是为您提供一个包含信息的数组,无论是否持有当前密钥。你试着尽可能短地按下按钮,但你的循环在时间上真的很快。因此,要解决此问题,您可以使用事件系统的键控功能,当您按下按钮时,该功能会为您提供 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_常量都是键码。此外(如果您考虑游戏(,扫描码有利于玩家移动,键控事件适用于 GUI 元素

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

希望你能理解!拉索洛兹

类似于这个问题(它实际上询问鼠标按钮,但键盘也是如此(:

不要使用 keyboardState,请使用 SDL 事件。SDL 每按下一个按钮将只触发一个事件,而快速while循环可以在单次按键期间触发多次。

尽管

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

从文档中:

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

否则,状态数组将不会更新,并显示您正在经历的结果。

注意

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

编辑

更新

问题后更新。

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

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

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

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

我将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向下箭头。如果键是右箭头,则count增加一个。

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

我按几次右键后的输出:

0
1
2
3
4

然后左边:

4
3
2
1
0