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.
Related
I tried drawing a '1' texture at mouse coordinates when I press the 1 key:
switch (e.type)
{
case SDL_QUIT:
{
quit = true;
break;
}
case SDL_KEYDOWN:
{
switch (e.key.keysym.sym)
{
case SDLK_1:
{
SDL_Rect rect = {e.motion.x - 8, e.motion.y - 8, 16, 16};
SDL_RenderCopy(gRenderer, gT[3], NULL, &rect);
printf("1\n");
break;
}
}
break;
}
}
I cannot comprehend why this doesn't work.
gT[3] is the texture of the '1'.
I thought maybe it is because its e.key.keysym.sym but I'm not sure.
SDL_Event::motion is only valid when SDL_Event::type is SDL_MOUSEMOTION.
Stop trying to use SDL_Event::motion when SDL_Event::type is SDL_KEYDOWN, perhaps by recording the X and Y coordinates of the most recent SDL_MOUSEMOTION event and using those instead.
I'm developing a simple game in OpenGL and using the SDL library to handle my input.
The game uses several keyboard buttons for input. Two arrow keys, the space bar, the 'r' button to reset the game and escape to close it.
What's currently happening is that the output or the methods being called when these buttons are pressed when I first launch the game are mismatched. (i.e left arrow performs the right arrow's method). This random behaviour ends once I press each of the buttons the game uses apart from the escape button once.
I'm wondering if there is something I'm missing as it feels like I have to 'assign' each button on each launch of the game. I've provided my input handling code below.
if (e.type == SDL_KEYDOWN) {
switch (e.key.keysym.sym) {
case SDLK_ESCAPE:
m_isClosed = true;
break;
case SDLK_SPACE:
m_selection = true;
break;
case SDLK_LEFT:
m_leftPressed = true;
break;
case SDLK_RIGHT:
m_rightPressed = true;
break;
case SDLK_r:
m_reset = true;
break;
}
}
if(e.type == SDL_KEYUP)
switch (e.key.keysym.sym)
{
case SDLK_LEFT:
m_leftPressed = false;
m_keydown = false;
break;
case SDLK_RIGHT:
m_rightPressed = false;
m_keydown = false;
break;
case SDLK_SPACE:
m_selection = false;
m_keydown = false;
break;
case SDLK_r:
m_reset = false;
m_keydown = false;
break;
}
}
Each event handler simply sets a boolean used for the game logic in another class. If you think you need to see anymore code in order to help me let me know.
It turns out that I had not initialised the booleans corresponding to the button listeners to false. A silly mistake but I thought I should provide the solution nonetheless.
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.
[SOLVED]I'm following this tutorial (video) and I'm at this very moment. I had no problems with compiling, but when it comes to running the program it's just flashing for a second and turning off. So I've run the debugger and I found a segmentation fault from SDL_DisplayFormat in my load_image function, when I comment off lines where I load images it works ok, but I can't find the reason of the problem.
[SOLUTION] I haven't fill the .bmp files that is why the SDL_DisplayFormat didn't work. Once filed with drawning, everything started to work.
Here is my code:
#include "game.h"
/*TODO
*stworzyć plik z blokami "blocks.bmp"
*stworzyć plik z tlem "background.bmp"
*uzywac jako tla do obrazkow 000,255,255
*
*/
game::game()
{
//zaladowanie ekranu
SDL_Init(SDL_INIT_EVERYTHING);
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGTH, 32, SDL_SWSURFACE);
//ladowanei obrazkow
//ustawianie polozenia i rozmiaru kamery
camera.x = camera.y = 0;
camera.w = SCREEN_WIDTH;
camera.h = SCREEN_HEIGTH;
//inicjalizacja kierunku w kotrym sie poruszamy
direction[0] = direction[1] = 0;
running = true;
block = (load_image("blocks.bmp"));
background = (load_image("background.bmp"));
}
game::~game()
{
SDL_FreeSurface(block);
SDL_FreeSurface(background);
SDL_Quit();
}
SDL_Surface* game::load_image (const char* filename)
{
SDL_Surface* tmp = SDL_LoadBMP(filename);
SDL_Surface* tmp2 = SDL_DisplayFormat(tmp);
//do odkomentowania kolorkey na razie zeby sprawdzac kolizje
// SDL_SetColorKey(tmp2, SDL_SRCCOLORKEY, SDL_MapRGB(screen->format, 0x00, 0xff, 0xff)
SDL_FreeSurface(tmp);
return tmp2;
}
void game::handleEvents()
{
SDL_Event event;
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
running = false;
return;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_LEFT:
direction[0] = 1;
break;
case SDLK_RIGHT:
direction[1] = 1;
break;
}
break;
case SDL_KEYUP:
switch(event.key.keysym.sym)
{
case SDLK_LEFT:
direction[0] = 0;
break;
case SDLK_RIGHT:
direction[1] = 0;
break;
}
break;
}
}
}
void game::start()
{
while(1)
{
handleEvents();
SDL_Flip(screen);
}
}
One advise up front: Provide complete, minimal code including compile instructions. Yours doesn't have a main() function and the class definition is missing too (game.h). After reconstructing this and running it in a debugger, I find that the first SDL_LoadBMP() returns NULL (maybe that's the same at yours, but here it's definitely the case that I don't have the according files), so I have no clue if you have the same problem or not.
Now, what you can try to fix this is to check returnvalues. In this case, try this:
SDL_Surface* tmp = SDL_LoadBMP(filename);
if(!tmp)
throw std::runtime_error("SDL_LoadBMP() failed");
Using a unique_ptr or some custom wrapper class is also a good advice in order to not leak resources.
I am working through the SDL tutorials over at http://lazyfoo.net/SDL_tutorials/index.php and I'm stuck on tutorial 8 where I'm working with key presses.
I'm using the following code:
//Our Main application loop
while(!quit){
if(SDL_PollEvent(&curEvents)){
if(curEvents.type == SDL_QUIT){
quit = true;
}
//If a key was pressed
if( curEvents.type == SDL_KEYDOWN )
{
//Set the proper message surface
switch( curEvents.key.keysym.sym )
{
case SDLK_UP:
message = upMessage;
break;
case SDLK_DOWN:
message = downMessage;
break;
case SDLK_LEFT:
message = leftMessage;
break;
case SDLK_RIGHT:
message = rightMessage; break;
default:
message = TTF_RenderText_Solid(font, "Unknown Key", textColor);
break;
}
}
}
if( message != NULL )
{
//Apply the background to the screen
applySurface( 0, 0, background, screen );
//Apply the message centered on the screen
applySurface( ( SCREEN_WIDTH - message->w ) / 2, ( SCREEN_HEIGHT - message->h ) / 2, message, screen );
//Null the surface pointer
message = NULL;
}
//Update the screen
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
}
Where works fine, the default case is reached, for everything BUT pressing the arrow keys. I was wondering if someone could spot what I'm doing wrong.
I discovered the error which was not in the code posted above. The error was that for the arrow keypress messages, I used RenderText before the font was opened. By the time the posted code block was reached, font had already been opened and is the reason why that message shows.