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

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

Related

Check if left mouse button is held down?

So I am just getting myself into coding using C++ and after learning a bit I started coding an auto clicker. I have already made it so you can toggle the auto clicker using a button on your keyboard or something of that sort, but that isn't what I'm looking for.
The problem I'm having is that I can't seem to find a way to check if the left mouse button is held down. If it is held down, then keep on clicking until the left mouse button is no longer held.
Also, I have looked around a lot to see how to do this, and I have gotten a few different things I should do, but none of them work. I was told to use:
if((GetKeyState(VK_LBUTTON) & 0x100) != 0)
from this answer here, but I also found that people were told to use:
if(GetKeyState(VK_LBUTTON) & 0x8000
Sadly I can't find where I found this in my history.
I have tried getting the key state of WM_LBUTTONDOWN but it doesn't seem to pick up the mouse button is being pressed.
I can get it working by checking the key state of VK_LBUTTON, but that only checks to see if the left mouse button has been pressed, not held. So it just continuously clicks until you break the while loop or close the program.
Not really worth putting this down but my 12 am self, thought that checking if the left button had been pressed (like the one before), it would set a boolean value to true and then keep on clicking. But after that, I couldn't get it to stop like before. Now looking back at the code, I understand why it wasn't working.
while (1) {
if(GetKeyState(WM_LBUTTONDOWN)) {
Sleep(delay);
mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);
std::cout << "Clicked" << endl;
}
if (GetKeyState(VK_ESCAPE)) {
break;
}
}
Like I said before, I have tried all different combinations to try and get this to work. But I can't see if the left mouse button is held down. I hope someone has the answer and can help me out and other people out. Anyways, thanks and have a nice day.
GetKeyState() takes a virtual key code as input, but WM_LBUTTONDOWN is not a virtual key code, it is a window message. Use VK_LBUTTON instead.
Also, GetKeyState() relies on a state machine that is local to the calling thread. That state machine is updated only when the thread processes keyboard/mouse window messages from its message queue. Your code has no such message processing. For what you are attempting, use GetAsyncKeyState() instead.
Also, mouse_event() is deprecated and has been for a long time. Use SendInput() instead.
Try this:
while (GetAsyncKeyState(VK_ESCAPE) >= 0)
{
if (GetAsyncKeyState(VK_LBUTTON) < 0)
{
Sleep(delay);
INPUT input[2] = {};
input[0].type = INPUT_MOUSE;
input[0].mi.dx = x;
input[0].mi.dy = y;
input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
input[1] = input[0];
input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(2, input, sizeof(INPUT));
std::cout << "Clicked" << endl;
}
}
I'm not sure whether I understood the q correctly .. but for checking the pressed state you have always to check the lowest bit in the returned high-byte of GetKeyState(VK_LBUTTON) (see GetKeyState() in MSDN-- means:
bool leftButtonPressed = 0 != (GetKeyState(VK_LBUTTON) & 0x800);
WM_LBUTTONDOWN is the message that is send to a window reporting a Left-button-down-event in the client area -- so using that in the GetKeyState() function is wrong. Try
const int delay = 50;
while (1)
{
if (GetKeyState(VK_LBUTTON) & 0x800)
{
Sleep(delay);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0,0, 0, 0);
std::cout << "Clicked" << std::endl;
}
if (GetKeyState(VK_ESCAPE) & 0x800)
{
break;
}
}

SFML Input Not responsive at start

I am making a 2D game where we are supposed to control the character through arrow keys.
if((win.GetInput().IsKeyDown(sf::Key::Down)))
{
y = y + Speed;
}
if((win.GetInput().IsKeyDown(sf::Key::Left)))
{
x = x - Speed;
}
I have set Speed to 10. And then a i use the Sprite.SetPosition(x,y) to actually animate my character.
Everything works fine. But the problem is whenever i press an arrow key, the character moves for 1/2 seconds, stops for about 1/2 seconds and then moves again smoothly. This happens whenever i press any arrow key.
And yes, i am using a while loop on top to handle multiple events simultaneously.
I hope my question was clear enough. Please help me out!
Thanks.
I think you're not handling events the right way. What you're doing here is checking on each event (which could be keyboard input or not) whether the sf::Key::Down key is pressed (and the same for sf::Key::Left).
Firstly, it's not effective, because you don't get the result you want.
Secondly, it performs useless checks admitting that the events could be mouse moves, mouse clicks or anything else : checking whether those keys are pressed in such cases is pointless for your program.
I can't see your whole code, but you should try something of this taste as your main loop :
bool isMovingLeft = false;
bool isMovingDown = false;
sf::Event event;
while (win.IsOpen())
{
// While window catches events...
while(win.GetEvent(event))
{
// If the caught event is a click on the close button, close the window
if (event.Type == sf::Event::Closed)
win.Close();
// If it's a key press, check which key and move consequently
else if (event.Type == sf::Event::KeyPressed)
{
if(event.Key.Code == sf::Key::Left)
isMovingLeft = true;
else if(event.Key.Code == sf::Key::Down)
isMovingDown = true;
}
// If it's a key release, stop moving in the following direction
else if (event.Type == sf::Event::KeyReleased)
{
if(event.Key.Code == sf::Key::Left)
isMovingLeft = false;
else if(event.Key.Code == sf::Key::Down)
isMovingDown = false;
}
}
// Now that we have caught events, we move the lil' thing if we need to.
if(isMovingLeft)
x = x - SPEED;
if(isMovingDown)
y = y - SPEED;
win.Clear();
// Draw things on the screen...
win.Display();
}
In this code, the whole process is split in two parts :
We first intercept the user input to see if we need to change the moving state of the thing.
Then, once every event has been caught and thoroughly analyzed, we move the thing if it has to. It is done through two bools (that you may need to increase to four if you want a four-direction control. If you want to handle diagonal directions, it would be wiser to use an enum than eight bool, which begins to be rather memory-consuming for such a simple task.)
Note : you will maybe notice that I changed "Speed" to "SPEED". I can't see if it was a define, a const var or simply a var from the code you have given, but the best option would be one of the two first ones. I prefer using #define for such things, to make constants easily reachable (as they're put in the preprocessor) and the fully capped writing make it more differentiable from classic vars once in the code. But that's just coding style we're talking of here :)

Am I using the getch() function from the ncurses library incorrectly?

I am writing a Pacman game in c++ using the ncurses library, but I am not able to move the Pacman properly. I have used getch() to move it it up, down, left and right, but it only moves right and does not move anywhere else when I press any other key.
This is a code snippet for moving up. I have written similar code with some conditions altered accordingly for moving left, right and down.
int ch = getch();
if (ch == KEY_RIGHT)
{
int i,row,column;
//getting position of cursor by getyx function
for (i=column; i<=last_column; i+=2)
{
//time interval of 1 sec
mvprintw(row,b,"<"); //print < in given (b,row) coordinates
//time interval of 1 sec
mvprintw(row,(b+1),"O"); //print "O" next to "<"
int h = getch(); //to give the option for pressing another key
if (h != KEY_RIGHT) //break current loop if another key is pressed
{
break;
}
}
}
if (condition)
{
//code to move left
}
Am I using getch() wrong, or is there something else I have to do?
Many of the "special" keys on a keyboard -- Up, Down, Left, Right, Home, End, Function keys, etc. actually return two scan codes from the keyboard controller back to the CPU. The "standard" keys all return one. So if you want to check for special keys, you'll need to call getch() twice.
For example up arrow is first 224, then 72.
261 is consistent with KEY_RIGHT (octal 0405 in curses.h). That tells us at least that keypad was used to allow getch to read special keys.
The fragment shown doesn't give clues to how it was incorporated into the rest of the program. However, the use of getch in a loop is likely a source of confusion, since on exiting the loop the value is discarded. If you expect to do something different (from KEY_RIGHT), you could use ungetch to save the (otherwise discarded) value within the loop, e.g.,
if (h != KEY_RIGHT) //break current loop if another key is pressed
{
ungetch(h); //added
break;
}
Doing that will allow the next call to getch to return the key which exits the loop.

How I can prevent every key being sent to KeyboardUpFunc in GLUT?

I'm fairly new to OpenGL, and I am writing a simple game in 2D, for fun. However, I ran into an issue I am having a hard time wrapping my head around.
It seems that whenever my keyboardUpFunc is called, that not only the key that has actually come up sent to the function, but every single key currently being pressed as well.
I'm using a simple key buffer to manage the keys, keyUp marks the key as up and is only called in this function. keyDown is called in my keyboardFunc. isDown returns a boolean value of whether or not the key is pressed. Take this code for example:
#include <iostream>
...
void keyboardUp(unsigned char key, int x, int y)
{
keys.keyUp(key);
if (keys.isDown('s') == false)
{
std::cout << "It's resetting s as well!" << std::endl;
}
// reset acceleration here, for each key
if ( (key == 'w') || (key == 's') )
{
yStep = 0.1;
}
if ( (key == 'a') || (key == 'd') )
{
xStep = 0.1;
}
std::cout << key << " is now up." << std::endl;
}
If you run this code, if you for example, hold S and D, then release the D key, you will note that S has been marked as up too, since this is the only location keyUp is being called.
Assuming my keyBuffer code is working correctly (and it is, but let me know if you want me to post it...), is there any way to get around this? Where if you were holding a key, and then pressed another key, the application would go back to what you were doing when you were just holding the original key? Instead of marking both as up? Or is this not feasible with GLUT?
Not very clear what is going wrong.. But where/how exactly are you calling this function ?? Directly in the Main Game loop, or are you checking certain conditions in an 'update' function. I ask because you need to check for input every run of the infinite loop, and if you are using a boolean to determine if a key is down, you should essentially reset it after its corresponding action has been performed. Anyway, just my 2 cents.
I changed my implementation of the keyboard buffer, and what I was describing above now works. The difference is that before I was using a vector to pile on the keys that were being pressed, whereas now, I am using a fixed-size array of boolean values.
Apparently, a vector implementation of a key buffer won't work properly with GLUT.

OpenGL pausing problems

I have the following function that is used as the glutKeyboardFunc function parameter:
void handleKeypress(unsigned char key, //The key that was pressed
int x, int y) { //The current mouse coordinates
switch (key) {
case 27: //Escape key
exit(0); //Exit the program
}
if (key == 'a')
{
moveCircleLeft(0);
}
if (key == 'w')
{
moveCircleUp(0);
}
if (key == 's')
{
moveCircleDown(0);
}
if (key == 'd')
{
moveCircleRight(0);
}
}
Now I will show you moveCircleLeft as an example of how the moveCircle functions are written:
void moveCircleLeft(int x)
{
characterX = characterX - 0.1;
glutPostRedisplay();
x++;
if (x < 10)
{
glutTimerFunc(10, moveCircleLeft, x);
}
}
The other moveCircle functions work similar and the only differences between the four functions is whether its plus of minus 0.1 or if it is characterY as opposed to characterX.
Now, here is the problem:
All the directions are moving in the right direction but, there is a delay/pause in movement of the character when I try to press keys. So, if I press just the d key (moveCircleRight) it moves right a little, stops a for a small amount of time, then moves across the screen in that direction at a constant speed with no pauses. Then if I change to a different key it pause for a little bit before changing directions then moves at a constant speed in that direction.
Any suggestion around this would be appreciated!
Create a boolean variable for each key (preferably an array). Then use KeyDown/KeyUp instead of KeyPress (i believe in GLUT its something like KeyboardUpFunc and KeyboardFunc, but cant remember now). On KeyDown, set the appropriate variable to true, on KeyUp, set it to false. Now you probably have an Idle function or something like that. Call there your "move" functions based on the boolean variables. There should be no pauses now. Beware though that you might need to keep track of the time elapsed between frames and scale the distance moved appropriately.