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
Related
I want to get mouse wheel up / down scroll events.
I got this so far:
switch (uMsg)
{
case WM_MOUSEWHEEL:
if (wParam < 0)
{
g_pMenu->SelectedOption--;
}
else
{
g_pMenu->SelectedOption++;
}
break;
}
but this does the same thing when I scroll up and down. What am I doing wrong?
Solved it. Working code:
switch (uMsg)
{
case WM_MOUSEWHEEL:
if ((short)HIWORD(wParam) < 0)
{
g_pMenu->SelectedOption--;
}
else
{
g_pMenu->SelectedOption++;
}
break;
}
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.
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.
I've got a little Problem with SDL:
When starting my Program (on Archlinux 64bit), SDL_event gives me some SDL_KEYDOWN events. The annoying thing with that is, that my Program Quits, when pressing ESCAPE.
Thats the Code:
void Input::update() {
[...]
while(SDL_PollEvent(event)) {
if (event != NULL) {
handleInput();
}
}
// SDL_PumpEvents(); Commented out, because of jrok's suggestion.
SDL_GetMouseState(&mousex, &mousey);
}
And:
void Input::handleInput() {
switch(event->type) {
case SDL_KEYDOWN:
setKey(event->key, true);
break;
case SDL_KEYUP:
setKey(event->key, false);
break;
case SDL_QUIT:
setQuit();
break;
default:
break;
}
}
setKey() sets an Element of an bool-Array to the given value (true/false).
Also, its not only the Escape-key. Often the other keys are pressed too.
Okey, sorry. SDL has not got any bugs, and the code, i have shown to you is right.
The problem was, that I didn't initialize my boolean Array. So there were Random values left...
That helped:
for (int i = 0; i < MAX_VAL; i++) {
keys[i] = false;
}
I've been using SDL_EventState as to disable and enable certain events. However for some reason once I set any state to either on or off, no where else in the program can I change it. To explain further, here is my loop.
void game::startLoop()
{
while(QUIT == false)
{
getRoomUpdate();
applySurface(-15, 280, zombie_lefthand, buffer);
applySurface(455, 280, zombie_righthand, buffer);
SDL_Flip(buffer);
SDL_WaitEvent(&gameEvent);
{
switch(gameEvent.type)
{
case SDL_QUIT:
QUIT = true;
break;
case SDL_KEYUP:
switch(gameEvent.key.keysym.sym)
{
case SDLK_LEFT:
if(isTwoWay == true)
SDL_EventState(SDL_KEYUP, 1);
startLoop();
log("Left key pressed.");
SDL_EventState(SDL_KEYUP, 0);
case SDLK_RIGHT:
if(isTwoWay == true)
SDL_EventState(SDL_KEYUP, 1);
startLoop();
log("Right key pressed.");
SDL_EventState(SDL_KEYUP, 0);
case SDLK_UP:
if(isTwoWay == false)
SDL_EventState(SDL_KEYUP, 1);
startLoop();
log("Up key pressed.");
SDL_EventState(SDL_KEYUP, 0);
default:
SDL_EventState(SDL_KEYUP, 0);
}
}
}
}
}
Perhaps if you tried someone else's code.
If it worked you could try to transform their code to something like yours(functionally wise)
Good Luck!
p.s. I think you should use SDL_ENABLE and SDL_DISABLE.