简体   繁体   中英

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

I've started writing an SDL2 program. I want integer count to go up one when the user presses the right arrow key, down one when user presses left.

#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;
}

Here's a sample of what's printed when I start the program and briefly tap right -- all within a second or two:

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

When I do a quick tap on the right arrow key, I want count to go up by just one, but instead it went from 0 to 4 .

Why?

How do I fix this problem?

Your problem is that you ask SDL for a keystate array, which, in your scenario, is not the best method. So, what does SDL do in this case? It simply gives you an array containing information, wheter the current key is being held or not. You try to press the button as short as you can, but your loop is really quick in time. So, to resolve this, you can use the event system's keydown feature, which gives you true, when you pressed down a button (its pair is the SDL_KEYUP for key released event).

The main difference is in the question: is the key being held, or I just pressed down and changed its state?

So, here is an example (use within the SDL_PumpEvent or 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...;
}
//...

Note that this method doesn't use scancodes, but keycodes. (They can produce different result than scancodes, due to different types of keyboards). SDLK_ constants are all keycodes. Moreover (if you think about games), scancodes are good for player movement, keydown events are good for GUI elements

Reference for more information: https://www.libsdl.org/release/SDL-1.2.15/docs/html/guideinputkeyboard.html

I hope you understand! Lasoloz

Similar to this Question (it actually asks about mouse buttons, but its the same for keyboards):

Instead of using keyboardState use the SDL events. SDL will trigger exactly one event per pressed button, while a fast while -loop can trigger multiple times during a single key-press.

Even though the example is far from being a minimal, complete one and it doesn't compile, I guess that the problem could be due to the lack of a call to SDL_PumpEvents .

As from the documentation :

Note: Use SDL_PumpEvents() to update the state array.

Otherwise, the state array won't be updated, with the results you are experiencing.

NOTE

That said, try to rely on events instead of the internal state array used to represent the keyboard.

EDIT

Updated after the question has been updated.

You should replace the if on SDL_PollEvent with a while , like the following one:

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

Otherwise, even if there are no events, it skips the if and goes through the other statements.
That means that the state of the keyboard won't be updated after the first key press if there are no events, but still you iterate over it.

See here for further details about how SDL_PollEvent works.

I changed the while loops to this:

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;
            }
        }
    }
}

If I understand it right, SDL_PollEvent fires only while address of object ev is true. If user presses a key down, it continues. If the key is left arrow, count goes down one. If key is right arrow, count increases one.

Now cout prints the way I hoped it would.

Output after I press right a few times:

0
1
2
3
4

Then left:

4
3
2
1
0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM