SDL 2.0 Key repeat and delay - c++

I'm having a problem with SDL 2.0 keyboard input in pong-like game. When I order to move to the left by pressing left arrow, it is processed by SDL_PollEvents() and responds correctly if the key was pressed once. However, if I keep the key pressed, I get a short delay (as long as Windows key repeat delay) before moving continuously.
Here is function processing events:
void Event::PlayerEvent (Player &player)
{
while (SDL_PollEvent (&mainEvent))
{
switch (mainEvent.type)
{
case SDL_KEYDOWN :
switch (mainEvent.key.keysym.sym)
{
case SDLK_ESCAPE :
gameRunning = false;
break;
case SDLK_LEFT :
player.moving = player.left;
break;
case SDLK_RIGHT :
player.moving = player.right;
}
break;
case SDL_QUIT :
gameRunning = false;
}
}
}
EDIT: After all, I managed to fix this issue by calling
SystemParametersInfo (SPI_SETKEYBOARDDELAY, 0, 0, 0) at the start of the program and SystemParametersInfo (SPI_SETKEYBOARDDELAY, 1, 0, 0) at the end, to return to standard key repeat delay.

For game movement, you would typically not use events, but rather use states.
Try using SDL_GetKeyboardState() outside of the event loop:
const Uint8* keystates = SDL_GetKeyboardState(NULL);
...
if(keystates[SDL_SCANCODE_LEFT])
player.moving = player.left;
else if(keystates[SDL_SCANCODE_RIGHT])
player.moving = player.right;

using SPI_SETKEYBOARDDELAY is not a good approach. This way your game will not be protable anymore since its only available on Windows.
Instead you should use like menthiond in an answer before SDL_GetKeyboardState.
Howeve be aware that you still have to collect the SDL_PollEvent. Otherwise SDL_GetKeyboardState will be always empty.
So it should be like this:
//...
SDL_Event sdlEvent;
while (SDL_PollEvent(&sdlEvent)) {
if (sdlEvent.type == SDL_QUIT) {
//..
}
}
const Uint8* keystates = SDL_GetKeyboardState(NULL);
if(keystates[SDL_SCANCODE_LEFT]) {
//...
}
if(keystates[SDL_SCANCODE_RIGHT]) {
/...
}
//...

simple as that
int vertical = 0;
int horizontal = 0;
float x = 500;
float y = 500;
float speed = 5.0;
in your sdl loop:
if (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
{
switch (event.key.keysym.sym)
{
case SDLK_LEFT: horizontal=-1; break;
case SDLK_RIGHT: horizontal = 1; break;
case SDLK_UP: vertical=-1; break;
case SDLK_DOWN: vertical=+1; break;
}
break;
}
case SDL_KEYUP:
{
switch (event.key.keysym.sym)
{
case SDLK_LEFT: horizontal = 0; break;
case SDLK_RIGHT: horizontal = 0; break;
case SDLK_UP: vertical = 0; break;
case SDLK_DOWN: vertical = 0; break;
}
break;
}
}
}
x += horizontal * speed;
y += vertical * speed;

Use the SDL_GetKeyboardState capturing outside of - while (SDL_PollEvent (&mainEvent)), that works fine.

Related

SDL Smoother movement in game

I have got a small problem with my game.
It is not a big deal but I'd love to sort this out.
So this is my input processing function:
void MainGame::processInput()
{
SDL_Event evnt;
while (SDL_PollEvent(&evnt)) {
switch (evnt.type) {
case SDL_QUIT:
_gameState = GameState::EXIT;
break;
case SDL_MOUSEMOTION:
break;
case SDL_KEYDOWN:
switch (evnt.key.keysym.sym) {
case SDLK_RIGHT:
player.movePlayer(1);
break;
case SDLK_LEFT:
player.movePlayer(2);
break;
}
}
}
}
and it works just fine, but as you can probably imagine, when I press an arrow key, it moves once (calls the player.movePlayer(); function once) then pauses for a fraction of a second and then continues to read input.
I don't really know how to word it but I hope you know what I mean.
You can also see this effect when you hold any letter in word.
Let's say you press W, it will display the first W instantly and then remaining ones after a fraction of a second something like that:
w wwwwwwwwwwwwwwwwwwwww
I really don't know how to explain it.
So the question is, is there a way to read the raw input from the keyboard so the function will be called continuously straight away?
I often use code like this:
bool keyboard[1<<30]; // EDIT: as noted in the comments this code might not be the most efficient
and in the event loop
while (SDL_PollEvent(&evnt)) {
switch (evnt.type) {
case SDL_QUIT:
_gameState = GameState::EXIT;
break;
case SDL_MOUSEMOTION:
break;
case SDL_KEYDOWN:
keyboard[evnt.key.keysym.sym] = true;
break;
case SDL_KEYUP:
keyboard[evnt.key.keysym.sym] = false;
break;
}
step();
}
and the step function:
void step() {
if(keyboard[SDLK_RIGHT]) player.movePlayer(1);
else if(keyboard[SDLK_LEFT]) player.movePlayer(2);
}

C++ & SDL: SDL event repeats endlessly

I have this code which is a part of a simple SDL-based game:
SDL_PollEvent(&event);
switch (event.type){
case SDL_QUIT:
quit = true;
break;
case SDL_KEYDOWN: //Det här är för när en knapp trycks ner...
switch (event.key.keysym.sym){
case SDLK_RIGHT:
player1.x_vel = 5.5;
std::cout<<"OOOO\n";
break;
case SDLK_DOWN:
player2_sprite.src.x = 58;
break;
}
break;
case SDL_KEYUP: //Det här är för när en knapp släpps upp..
switch (event.key.keysym.sym){
case SDLK_UP:
std::cout<<"haaaaa\n";
if(player1.canJump){
player1.y_vel = -7.5;
player1.canJump = false;
}
break;
case SDLK_DOWN:
std::cout<<"BBBB\n";
player2_sprite.src.x = 0;
break;
}
break;
}
Now, upon the release of the UP or DOWN arrow key, the release-event keeps triggering forever until any other event is handled. What am I doing wrong here?
You should be checking the return value of SDL_PollEvent. It returns 1 if there is an event to handle, and 0 if there is not. Normally, you handle events in a loop like this (copied from the documentation here):
while (1) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
// handle your event here
}
// do some other stuff here -- draw your app, etc.
}
What is probably happening in your case, is you are handling the event even when SDL_PollEvent returns 0. The event object is left unmodified in that case, so you keep handling the most recent event over and over.

SDL_event list getting long and messy?

What is a good way of doing SDL event handling?
You usually have the:
while (SDL_PollEvent(&event)) {
//Handles all the events when in the menu screen...
eventsMenu(event);
}
Problem is, when you get going with a game, there's usually a mess of a list of controls that you can do, like up and down detetion for many different keys. And I'm wondering weither the method I'm using is efficient and clean. Or if I should approach it diffrently...
I have a function pointer in the mainLoop, that can be assigned to quickly change how the events will be handled. (Say I swap to another function, and it will use those events)
However, I think the list of events get messy anyways, so I tried adding regions to break it up. Is that a good idea? And yeah, just want some input if I'm on the right path to a readable code.
void Window::eventsMenu(SDL_Event event) {
switch (event.type) {
#pragma region "Button Down"
case SDL_MOUSEBUTTONDOWN: {
//printf("Mouse button down!\n");
glClearColor(0.1, 0.1, 0.1, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = true;
}
break;
}
#pragma endregion;
#pragma region "Button Up"
case SDL_MOUSEBUTTONUP: {
//printf("Mouse button up!\n");
glClearColor(0, 0, 0, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = false;
}
break;
}
#pragma endregion;
#pragma region "Mouse Motion"
case SDL_MOUSEMOTION: {
//printf("Mouse moved!\n");
if (mouseButtonLeft) {
rotX += event.motion.xrel;
rotY += event.motion.yrel;
}
break;
}
#pragma endregion;
#pragma region "Mouse Wheel"
case SDL_MOUSEWHEEL: {
if (event.wheel.y != 0) {
musicVolume += ((event.wheel.y > 0) ? 1 : -1);
if (musicVolume > 100) {
musicVolume = 100;
}
else if (musicVolume < 0) {
musicVolume = 0;
}
Mix_VolumeMusic(musicVolume);
printf("Volume: %i%c\n", musicVolume, '%');
}
if (event.wheel.y > 0) {
//printf("Scroll forward!\n");
}
else {
//printf("Scroll backward!\n");
}
break;
}
#pragma endregion;
#pragma region "Key Down"
case SDL_KEYDOWN: {
printf("Button [%s] pressed\n", SDL_GetKeyName(event.key.keysym.sym));
switch (event.key.keysym.sym) {
case SDLK_1: {
Mix_PlayChannel(-1, sound1, 0);
break;
}
case SDLK_2: {
Mix_PlayChannel(-1, sound2, 0);
break;
}
}
break;
}
#pragma endregion;
case SDL_QUIT: {
running = false;
}
}
}
Two suggestions :
Remove the braces ( { and } ) around the case labels. You don't need them unless you need a new stack.
My second suggestion is to split things into function. Even if it will only be called from within switch. Putting things into several functions helps makes the code easier to read and understand.
So for instance :
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
HandeMouseButton( event );
break;
void HandeMouseButton( const SDL_Event &event )
{
if ( event.type == MOUSEBUTTONDOWN )
{
glClearColor(0.1, 0.1, 0.1, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = true;
}
else if ( event.type == MOUSEBUTTONUP )
glClearColor(0, 0, 0, 1);
if (event.button.button == SDL_BUTTON_LEFT) {
mouseButtonLeft = false;
}
}
}
And generally ( slightly opinion based ) ; if you need to use #pragma once to make the code readable, it can ( and should ) be split into more function

Change game controls in game

I'm programming game in C++ and Allegro 5, and I wanted to make controls settings. So if player wants, he can change controls in settings menu (in game).
I have this code:
while(!exit)
{
ALLEGRO_EVENT ev;
al_wait_for_event(e_queue, &ev);
if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_DOWN:
//player goes down...
break;
case ALLEGRO_KEY_UP:
//player goes up...
break;
case ALLEGRO_KEY_LEFT:
//player goes left...
break;
case ALLEGRO_KEY_RIGHT:
//player goes right...
break;
case ALLEGRO_KEY_SPACE:
break;
default:
break;
}
}
...
}
I thought, that I can make variable of ALLEGRO KEY CODE (i dont know if type like this exists) type, which has key code (for example ALLEGRO_KEY_LEFT), and than replace this code with:
...
allegro key code keyUP;
allegro key code keyDOWN;
...
while(!exit)
{
ALLEGRO_EVENT ev;
al_wait_for_event(e_queue, &ev);
if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
switch(ev.keyboard.keycode)
{
case keyDOWN:
break;
case keyUP:
break;
case keyLEFT:
break;
case keyRIGHT:
break;
case keySPACE:
break;
default:
break;
}
}
}
and add in settings something like this:
//set 'UP' key
switch(ev.keyboard.keycode)
{
case ALLEGRO_KEY_A:
keyUP = ALLEGRO_KEY_A;
break;
case ALLEGRO_KEY_B:
keyUP = ALLEGRO_KEY_B;
break;
case ALLEGRO_KEY_UP:
keyUP = ALLEGRO_KEY_UP;
break;
...
}
}
The point is, I don't know what type store things like ALLEGRO_KEY_UP, DOWN ...
Thanks!
I would have two maps, one that maps a function name to a function, and one that maps a key to either the function name or the function. When the user want to remap keys, change the second map.
It could be something like
std::unordered_map<std::string, std::function<void()>> function_map;
function_map["up"] = std::bind(&functionForUp);
function_map["down"] = std::bind(&functionForDown);
function_map["jump"] = std::bind(&functionForJump);
// etc.
std::unordered_map<int, std::string> key_map;
key_map[ALLEGRO_KEY_UP] = "up";
key_map[ALLEGRO_KEY_DOWN] = "down";
key_map[ALLEGRO_KEY_SPACE] = "jump";
// etc.
To call the function for a specific key, use e.g.
function_map[key_map[ev.keyboard.keycode]]();
This allows you to change the key_map at will:
key_map[ALLEGRO_KEY_J] = "jump";
References:
std::unordered_map
std::function
std::bind
All these Key Codes should be defined numbers. So maybe try using an integer.
int key = ALLEGRO_KEY_DOWN;
This should work.

Event handling bug with SDL

I am making a snake game using SDL and C++, and I found a bug involving events.
In this game, there are two important functions for now : the play function (the "main" function of the game) and the pause function. When the P key is pressed, the play function calls and executes the pause function. In it, you have two possibilities: either continue or quit the game.
Now, here is the problem. When I try to click "Continue game", the game flashes for a split second and comes back to the pause menu. When I keep the mouse button down, I realize the game itself is frozen until an event of any kind happens. If it does, it just comes back to where it was.
After some tests, I also noticed that the bug was due to the SDL_PollEvent function. So I changed this function in the play function to SDL_WaitEvent, which put the game back when I asked him to. However, another problem is created: since, in my play function, there is a timer that uses SDL_GetTicks, the game needs a trigger event to be fluid. For example, when I constantly move the cursor in any direction, the game can run. When I stop, it freezes.
Is there a way to fix this bug?
Here is the event handler in the play function:
SDL_PollEvent(&event); // Handle events
switch(event.type)
{
case SDL_QUIT:
quit = true;
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym) // Change direction at certain key press
{
case SDLK_s:
currentDirection = SNAKE_DOWN;
break;
case SDLK_w:
currentDirection = SNAKE_UP;
break;
case SDLK_a:
currentDirection = SNAKE_LEFT;
break;
case SDLK_d:
currentDirection = SNAKE_RIGHT;
break;
case SDLK_ESCAPE: // Quit if escape key pressed
quit = true;
break;
case SDLK_p:
if (pause(screen) == false)
quit = true;
break;
break;
default: ; // Code to block the compiler's -Wall flag (does not do anything to the program itself)
}
break;
}
And here is the complete pause function:
bool pause(SDL_Surface *screen)
{
SDL_Surface *pauseMessage, *continueButton, *quitButton;
SDL_Rect posPauseMessage, posContinueButton, posQuitButton;
TTF_Font *fipps;
SDL_Color color = {255, 255, 255};
SDL_Event event;
int x = 0, y = 0;
bool quit = false, returnFalse = false;
fipps = TTF_OpenFont("fipps.ttf", 50);
pauseMessage = TTF_RenderText_Blended(fipps, "PAUSED", color);
continueButton = TTF_RenderText_Blended(fipps, "Continue Game", color);
quitButton = TTF_RenderText_Blended(fipps, "Exit to menu", color);
posPauseMessage.x = (screen->w - pauseMessage->w) / 2;
posPauseMessage.y = 200;
posContinueButton.x = (screen->w - continueButton->w) / 2;
posContinueButton.y = posPauseMessage.y + 200;
posQuitButton.x = (screen->w - quitButton->w) / 2;
posQuitButton.y = posContinueButton.y + 100;
while (!quit)
{
SDL_WaitEvent(&event);
switch(event.type)
{
case SDL_QUIT:
returnFalse = true;
quit = true;
break;
case SDL_MOUSEBUTTONUP:
x = event.button.x;
y = event.button.y;
if ((x > posContinueButton.x) && (x < posContinueButton.x + continueButton->w) && (y > posContinueButton.y) && (y < posContinueButton.y + continueButton->h))
quit = true;
else if ((x > posQuitButton.x) && (x < posQuitButton.x + quitButton->w) && (y > posQuitButton.y) && (y < posQuitButton.y + quitButton->h))
returnFalse = true;
quit = true;
break;
}
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
SDL_BlitSurface(pauseMessage, NULL, screen, &posPauseMessage);
SDL_BlitSurface(continueButton, NULL, screen, &posContinueButton);
SDL_BlitSurface(quitButton, NULL, screen, &posQuitButton);
SDL_Flip(screen);
}
TTF_CloseFont(fipps);
SDL_FreeSurface(pauseMessage);
SDL_FreeSurface(continueButton);
SDL_FreeSurface(quitButton);
if (returnFalse)
return false;
return true;
}
Thanks to #jordsti, I found a solution : I need to put the SDL_PollEvent as a condition for a while loop, which contains the switch block.