sdl getting continuously key pressing - c

I am currently programming a simple game in SDL (PONG clone) where i must have a continuously key pressing.
The problem I have is that when i press a button it reacts, waits a short period and then continues to continuously key pressing.
I wonder how i get that little delay to go away.
Here is my code:
while (running)
{
while (SDL_PollEvent(&event))
{
//update the game
if (event.type == SDL_QUIT)
{
quit_game();
running = false;
}
if(event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_DOWN:
move_paddle(UP);
break;
case SDLK_UP:
move_paddle(DOWN);
break;
default:
break;
}
}
}
draw_game();
move_ball();
SDL_Delay(15);
}

The first thing i did was i wanted to remove the small delay and get the movement of the paddle smoother. I introduced 2 new global bool variables to keep track on which key that was pressed down.
bool key_up = false;
bool key_down = false;
I initiate them with the value false because no key is currently pressed.
In my switch I set the value for these boolean variables to match what is currently pressed down.
if(event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_DOWN:
key_up = true;
key_down = false;
break;
case SDLK_UP:
key_down = true;
key_up = false;
break;
default:
break;
}
}
Then in the first while-loop i make two if-cases for the two keys that can be pressed and call the movement function in these if-cases.
if (key_up)
move_paddle(UP);
else if (key_down)
move_paddle(DOWN);
When i did this the delay disappeared and the paddle movement was smooth but it also generated another probelm, the paddle could not stay still in one place (except the boundraries). I then realised that the code is only looking for a key that is pressed down and does not react to a key that is released.
I then figured out that if a key is released (in this case the UP key or the DOWN key) the two boolean values had to be set to false because no key was pressed down.
What I did was that I made another if-statement after the if(event.type == SDL_KEYDOWN) that would disable the movement of the paddle if no key was pressed down.
if (event.type == SDL_KEYUP)
{
key_down = false;
key_up = false;
}
When I implemented all of this in my code the game worked like a charm.
For those who are interested, here is my new final code:
int gameloop()
{
SDL_Event event;
bool running = true;
bool key_up = false;
bool key_down = false;
while (running)
{
while (SDL_PollEvent(&event))
{
//update the game
if (event.type == SDL_QUIT)
{
quit_game();
running = false;
}
if(event.type == SDL_KEYDOWN)
{
switch (event.key.keysym.sym)
{
case SDLK_DOWN:
key_up = true;
key_down = false;
break;
case SDLK_UP:
key_down = true;
key_up = false;
break;
default:
break;
}
}
if (event.type == SDL_KEYUP)
{
key_down = false;
key_up = false;
}
}
if (key_up)
move_paddle(UP);
else if (key_down)
move_paddle(DOWN);
move_ball();
ai_paddle_move();
draw_game();
printf("Player: %d | AI: %d\n", player, ai);
SDL_Delay(15);
}
return 1;
}

Related

Animate surface in SDL/SDL_gfx library

I'm recreating the game 'super hexagon' in c using sdl2 library.
I have drawn the circle in the middle of the application and the little cursor(not the mouse but the triangle around the circle) that is supposed to move around it when the player interact with the keyboard (left and right keys) like so:
void Game_run(struct Game *game)
{
//Previous rendering are cleared.
SDL_RenderClear(game->renderer);
//-------------------------------------------------------
if (filledCircleRGBA(game->renderer,SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 10,255, 255, 255, 255) != 0)
{
fprintf(stderr, "Error: the circle was not rendered\n");
SDL_DestroyRenderer(game->renderer);
}
arcRGBA( game->renderer, SCREEN_WIDTH/2, SCREEN_HEIGHT/2, 100, 0, 80, 255, 255, 255, 255 );
filledTrigonRGBA (game->renderer, CURSOR_POSITION_X-10, CURSOR_POSITION_Y, CURSOR_POSITION_X+10, CURSOR_POSITION_Y, CURSOR_POSITION_X, CURSOR_POSITION_Y+10, 255, 255, 255, 255);
SDL_RenderPresent(game->renderer);
//-------------------------------------------------------
//We are payng
game->state = PLAYING;
SDL_Event event;
SDL_bool GameRunning = SDL_TRUE;
while (GameRunning) //while the player didnt loose, the game is running
{
while (SDL_PollEvent(&event))
{
/* code */
switch (event.type)
{
//when a key is pressed
case SDL_KEYDOWN:
//Detecting the pressed key
switch (event.key.keysym.sym)
{
case SDLK_LEFT:
printf("Left key pressed\n");
break;
case SDLK_RIGHT:
printf("Right key pressed\n");
break;
default:
break;
}
break;
//when a key is unpressed
case SDL_KEYUP:
switch (event.key.keysym.sym)
{
case SDLK_LEFT:
printf("Left key up\n");
break;
case SDLK_RIGHT:
printf("Right key up\n");
break;
default:
break;
}
break;
//if the user quit the game
case SDL_QUIT:
//The game is over
GameRunning = SDL_FALSE;
game->state = GAME_OVER;
break;
default:
break;
}
}
}
}
I have already implemented the event listener to detect the user input.
But I have no idea how to animate those elements.
How should I approach the problem? I want to understand how animation works when i render elements using gfx functions..do i need to load the drawn elements into a SDL_Surface first?

Creating a FIFO Queue with Keypad Inputs

I'm trying to implement a FIFO queue for keypad inputs but cannot seem to get it to work. I'm able to get the keypad inputs to appear on an LCD screen but that is all I'm able to do. I think the code is supposed to read a keypad input and push it into a queue, then pop the keypad input and read the value onto an LCD screen. Could anyone advise on why it doesn't display all the values I've pressed? Thank you.
#include "project.h"
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
// Keypad Variables
char KeyPad[4][4] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'},
};
char ReadKeyPad();
// FIFO Variables
struct FIFO {
char key;
struct FIFO *next;
};
struct FIFO KeyQueue[] = {
{0, &KeyQueue[1]}, //[0]
{0, &KeyQueue[2]}, //[1]
{0, &KeyQueue[3]}, //[2]
{0, &KeyQueue[4]}, //[3]
{0, &KeyQueue[5]}, //[4]
{0, &KeyQueue[6]}, //[5]
{0, &KeyQueue[7]}, //[6]
{0, &KeyQueue[8]}, //[7]
{0, &KeyQueue[0]} //[8]
};
struct FIFO *Head;
struct FIFO *Tail;
int KeyQueue_Size=0;
bool KeyQueue_IsEmpty();
bool KeyQueue_IsFull();
void KeyQueue_Push(char key);
char KeyQueue_Pop();
char ReadKeyPad();
int delay = 10;
int main(void)
{
Head = Tail = &KeyQueue[0];
char key;
CyGlobalIntEnable; /* Enable global interrupts. */
/* Place your initialization/startup code here (e.g. MyInst_Start()) */
LCD_Start();
LCD_ClearDisplay();
LCD_Enable();
for(;;)
{
/* Place your application code here. */
key = ReadKeyPad();
LCD_Position(1,8);
LCD_PutChar(key);
if (key !=0)
{
if (!KeyQueue_IsFull())
KeyQueue_Push(key);
}
//task for poping key
delay --;
if(delay <= 0 && !KeyQueue_IsEmpty())
{
KeyQueue_Pop();
switch (key)
{
case '1':
LCD_Position(0,1);
LCD_PrintString("You pressed 1");
break;
case '2':
LCD_Position(0,1);
LCD_PrintString("You pressed 2");
break;
case '3':
LCD_Position(0,1);
LCD_PrintString("You pressed 3");
break;
case '4':
LCD_Position(0,1);
LCD_PrintString("You pressed 4");
break;
case '5':
LCD_Position(0,1);
LCD_PrintString("You pressed 5");
break;
case '6':
LCD_Position(0,1);
LCD_PrintString("You pressed 6");
break;
case '7':
LCD_Position(0,1);
LCD_PrintString("You pressed 7");
break;
case '8':
LCD_Position(0,1);
LCD_PrintString("You pressed 8");
break;
case '9':
LCD_Position(0,1);
LCD_PrintString("You pressed 9");
break;
case 'A':
LCD_Position(0,1);
LCD_PrintString("You pressed A");
break;
case 'B':
LCD_Position(0,1);
LCD_PrintString("You pressed B");
break;
case 'C':
LCD_Position(0,1);
LCD_PrintString("You pressed C");
break;
case 'D':
LCD_Position(0,1);
LCD_PrintString("You pressed D");
break;
case '*':
LCD_Position(0,1);
LCD_PrintString("You pressed *");
break;
case '#':
LCD_Position(0,1);
LCD_PrintString("You pressed #");
break;
case '0':
LCD_Position(0,1);
LCD_PrintString("You pressed 0");
break;
delay = 10;
}
CyDelayUs(100);
}
}
}
bool KeyQueue_IsEmpty()
{
if(KeyQueue_Size == 0) return true;
return false;
}
bool KeyQueue_IsFull()
{
//if (Head == Tail) return truel
if(KeyQueue_Size ==9) return true;
return false;
}
void KeyQueue_Push(char key)
{
Head->key=key;
Head = Head->next;
KeyQueue_Size ++;
}
char KeyQueue_Pop()
{
char key;
key = Tail->key;
Tail = Tail->next;
KeyQueue_Size --;
return key;
}
char ReadKeyPad()
{
int i;
char key;
uint8_t col;
for (i=0; i<4; i++)
{
key = 0;
ROW_Write(1<<i);
col=COL_Read();
//LCD_Position(1,0);
//LCD_PrintNumber(key);
if (col & 0x01) key=KeyPad[i][0];
if (col & 0x02) key=KeyPad[i][1];
if (col & 0x04) key=KeyPad[i][2];
if (col & 0x08) key=KeyPad[i][3];
if (key != 0) break;
}
return key;
}
Some problems in your code:
As pointed before, delay = 10; is not well placed
key variable of main function is set only at the beginning of for loop.
So a corrected code could be:
for(;;)
{
key = ReadKeyPad();
LCD_Position(1,8);
LCD_PutChar(key);
if (key !=0)
{
if (!KeyQueue_IsFull())
KeyQueue_Push(key);
}
//task for poping key
delay --;
if(delay <= 0 && !KeyQueue_IsEmpty())
{
/* don't forget to store what have been poped from fifo */
key = KeyQueue_Pop();
switch (key)
{
case '1':
LCD_Position(0,1);
LCD_PrintString("You pressed 1");
delay = 10;
break;
case '2':
LCD_Position(0,1);
LCD_PrintString("You pressed 2");
delay = 10;
break;
/*
.
.
.
*/
case '0':
LCD_Position(0,1);
LCD_PrintString("You pressed 0");
delay = 10;
break;
}
CyDelayUs(100);
}
}
Maybe you can write it shorter:
for(;;)
{
key = ReadKeyPad();
LCD_Position(1,8);
LCD_PutChar(key);
if (key !=0)
{
if (!KeyQueue_IsFull())
KeyQueue_Push(key);
}
//task for poping key
delay --;
if(delay <= 0 && !KeyQueue_IsEmpty())
{
/* don't forget to store what have been poped from fifo */
key = KeyQueue_Pop();
/* print what have been pressed */
LCD_Position(0,1);
LCD_PrintString("You pressed ");
LCD_Position(0,12);
LCD_PutChar(key);
delay = 10;
CyDelayUs(100);
}
}

SDL/C EventHandler continuous key

I'm trying to develop an event handler in C using SDL.
I wanted to be able to detect the following things: Key pressed, key released and key held down.
The problem is that keyboard auto-repeat spams pressed keys after a certain delay and at a given interval. To mitigate this I tried using SDL_EnableKeyRepeat(0, 0); which according to documentation should disable it?
As that failed I tried mapping SDL_GetKeyboardState(NULL); and it sort of works. But I'm having the issue that if i hold down a key for X seconds, it will spam ghost events for the same amount of time.
An example output from pressing esc, holding it down for a bit, and then releasing it is:
Pressed
HELD
HELD
HELD
...
RELEASED {it breaks down after first proper release}
PRESSED
RELEASED
HELD
PRESSED
RELEASED
HELD
...
RELEASED // it always ends on released.
The result from above is:
Pressed: 18;
Released: 21;
Held: 39.
Should be:
Pressed 1;
Released 1;
Held: say 20.
Not sure if I'm messing up my logic somehow, or if it's something with the actual built in mechanics of keyboard events.
Here is the code:
/**
Program Interface
void ev_handle_start() // Starts the Event Handler.
void ev_handle_stop() // Pauses the Event Handler.
**/
#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL/SDL.h>
#include "ev_handler.h"
#define PRESSED 1
#define RELEASED 0
#define TMR_FREQ 33
#define MOUSE_RIGHT 1
#define MOUSE_LEFT 3
#define EV_LEFT_CLICK 0
#define EV_LEFT_RELEASE 1
#define EV_RIGHT_CLICK 2
#define EV_RIGHT_RELEASE 3
#define EV_MOUSE_MAX 4
#define MAX_SCANCODES SDL_SCANCODE_MODE
#define EV_DELAY 70
//********************************************************************************************//
// VARIABLES
//********************************************************************************************//
SDL_Event e;
Uint8* keyboardState;
SDL_TimerID ev_timer = NULL; // Timer to callback the search of the code
bool ev_handler_active = false; // Used to determine if the ev_handler() is enabled or not
bool buttonState[EV_MOUSE_MAX]; // Button Stats, either pressed or released
bool evMouseEvent[EV_MOUSE_MAX]; // Used to flag if a mouse events
int evKeyEventStatus[MAX_SCANCODES];
bool evKeyStatePrev[MAX_SCANCODES]; //
bool evKeyStateCur[MAX_SCANCODES]; //
//********************************************************************************************//
// 1 FRONTEND
//********************************************************************************************//
//********************************************************************************************//
// 1.1 MOUSE
// Can only handle left and right mouse buttons.
//********************************************************************************************//
bool evButtonPressed(int button)
{ bool flag = button == SDL_BUTTON_RIGHT || button == SDL_BUTTON_LEFT;
if (!flag) {
printf("ERROR: isButtonPressed(int) received parameter of invalid type.\n");
return false;
}
return buttonState[button];
}
bool evLeftClick()
{
return evMouseEvent[EV_LEFT_CLICK];
}
bool evLeftRelease()
{
return evMouseEvent[EV_LEFT_RELEASE];
}
bool evRightClick()
{
return evMouseEvent[EV_RIGHT_CLICK];
}
bool evRightRelease()
{
return evMouseEvent[EV_RIGHT_RELEASE];
}
//********************************************************************************************//
// 1.2 KEYBOARD
//********************************************************************************************//
bool evKeyHeld(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 1 || evKeyEventStatus[sc] == 3;
}
bool evKeyPressed(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 1;
}
bool evKeyReleased(SDL_Scancode sc)
{
return evKeyEventStatus[sc] == 2;
}
//********************************************************************************************//
// 1.3 MANAGE
//********************************************************************************************//
void evSetup()
{
for (int i = 0; i < MAX_SCANCODES; i++) {
evKeyStatePrev[i] = 0;
}
evClearEvents();
}
int j;
void evClearEvents()
{
evMouseEvent[EV_LEFT_CLICK] = false;
evMouseEvent[EV_LEFT_RELEASE] = false;
evMouseEvent[EV_RIGHT_CLICK] = false;
evMouseEvent[EV_RIGHT_RELEASE] = false;
}
//********************************************************************************************//
// 2 BACKEND
//********************************************************************************************//
void ev_handler()
{
while(SDL_PollEvent(&e))
{
switch (e.type)
{
/*Skip unnecessary events*/
case SDL_KEYDOWN:
break;
case SDL_KEYUP:
break;
case SDL_MOUSEMOTION:
break;
case SDL_MOUSEBUTTONDOWN:
buttonState[e.button.button] = PRESSED;
if (e.button.button == SDL_BUTTON_LEFT) {
evMouseEvent[EV_LEFT_CLICK] = true;
}
else if (e.button.button == SDL_BUTTON_RIGHT) {
evMouseEvent[EV_RIGHT_CLICK] = true;
}
break;
case SDL_MOUSEBUTTONUP:
buttonState[e.button.button] = RELEASED;
if (e.button.button == SDL_BUTTON_LEFT) {
evMouseEvent[EV_LEFT_RELEASE] = true;
}
else if (e.button.button == SDL_BUTTON_RIGHT) {
evMouseEvent[EV_RIGHT_RELEASE] = true;
}
break;
case SDL_QUIT:
exit(0);
printf("Quit\n");
break;
default:
break;
}
keyboardState = SDL_GetKeyboardState(NULL);
for (int i = 0; i < MAX_SCANCODES; i++)
{
evKeyStateCur[i] = keyboardState[i];
int sum = evKeyStateCur[i] + evKeyStatePrev[i]*2;
switch (sum) {
case 0:
break;
case 1:
evKeyEventStatus[i] = 1;
break;
case 2:
evKeyEventStatus[i] = 2;
break;
case 3:
evKeyEventStatus[i] = 3;
break;
default: evKeyEventStatus[i] = 0; break;
}
evKeyStatePrev[i] = evKeyStateCur[i];
}
}
}
The problem was that SDL 2.0.4 had a bug where holding down a key sent key releases in-between key presses instead of continuous key presses. Downloading the latest version fixed the issue.

SDL - Why moving the mouse changes button state?

I'm having a problem with the Simple Directmedia Layer library. The following code draws a block on the screen when a mouse button is pressed:
SDL_Event event;
while(running){
while(SDL_PollEvent(&event)){
while(event.button.state == SDL_PRESSED){
SDL_PollEvent(&event);
//where to draw
boxRect.x = event.motion.x;
boxRect.y = event.motion.y;
//Draw to screen
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
// ...
}
// ...
}
It works fine until I move the mouse, why moving the mouse makes event.button.state untrue?
How can I work with both simultaneously (i.e. keep drawing while the button is pressed)?
The problem with your code is that you're calling SDL_PollEvent (documented here) twice. As said in the documentation:
If event is not NULL, the next event is removed from the queue and
stored in the SDL_Event structure pointed to by event.
Rearranging your code a bit, like getting rid of the second SDL_PollEvent, creating the proper flow for clicking, moving, releasing and extracting rendering from the input pumping should give you something like this:
SDL_Event Event;
while(running)
{
while(SDL_PollEvent(&Event))
{
switch(Event.type)
{
// Handle your drawing with a simple state machine:
case SDL_MOUSEBUTTONDOWN:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
if(stateMachine == Released)
{
// ... begin drawing
stateMachine = Dragging
}
}
break;
}
case SDL_MOUSEMOTION:
{
if(stateMachine == Dragging)
{
// ... update the extends of your rect
}
}
case SDL_MOUSEBUTTONUP:
{
if(Event.button.button == SDL_BUTTON_LEFT)
{
if(stateMachine != Released)
{
// ... finalize drawing... add the rect to a list? flush it?
stateMachine = Released;
}
}
}
case SDL_QUIT:
{
running = false;
break;
}
}
}
// Outside of your event pumping, update the graphics
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
event.button.state is a bit mask. It is declared as a char which can hold eight bits representing different states of the buttons.
typedef struct{
Uint8 type;
Uint8 state;
Uint16 x, y;
Sint16 xrel, yrel;
} SDL_MouseMotionEvent;
More information here,
http://www.libsdl.org/docs/html/sdlgetmousestate.html
http://www.libsdl.org/docs/html/sdlmousemotionevent.html
Thanks to emartel's answer, it's working as expected, here's the code:
SDL_Event event;
int drawing = 0;
while(running){
while(SDL_PollEvent(&event)){
switch(event.type){
//starts drawing when any mouse button is pressed
case SDL_MOUSEBUTTONDOWN:
drawing = 1;
boxRect.x = event.button.x;
boxRect.y = event.button.y;
break;
//keeps drawing while no button is released
case SDL_MOUSEMOTION:
if(drawing == 1){
boxRect.x = event.motion.x;
boxRect.y = event.motion.y;
}
break;
//stops drawing when any button is released
case SDL_MOUSEBUTTONUP:
drawing = 0;
break;
case SDL_QUIT:
running = 0;
break;
}
}
//draws to screen
if(drawing == 1){
SDL_FillRect(display,&boxRect,boxColor);
SDL_Flip(display);
}
}

C SDL Keyboard Events SDL_KEYUP Triggering When Key Is Down

I am using SDL in C, and I'm trying to make a character move on the screen when a key is pressed, and stop when it is released, but it seems as if the KEYUP event is triggering when the key is still being held down. If I remove the KEYUP section in pressed(), the characters will slide across the screen, but obviously won't stop on their own. If I leave the KEYUP section in, I have to press the key repeatedly to move them across the screen.
Any idea what I'm doing wrong?
Here's what I have:
...
int done = 0;
while (done==0)
{
pressed();
if (kenter == 1)
{
state = 1;
subscreen();
}
if (kescape == 1)
{
done = 1;
}
if (kup == 1)
{
playery += -2;
}
if (kdown == 1)
{
playery += 2;
}
if (kright == 1)
{
playerx += 2;
}
if (kleft == 1)
{
playerx += - 2;
}
...
int pressed ()
{
SDL_Event keyevent;
while (SDL_PollEvent(&keyevent))
{
switch(keyevent.type)
{
case SDL_KEYDOWN:
switch(keyevent.key.keysym.sym)
{
case SDLK_RETURN:
{
kenter = 1;
break;
}
case SDLK_ESCAPE:
{
kescape = 1;
break;
}
case SDLK_a:
{
ka = 1;
break;
}
case SDLK_s:
{
ks = 1;
break;
}
case SDLK_UP:
{
kup = 1;
break;
}
case SDLK_DOWN:
{
kdown = 1;
break;
}
case SDLK_RIGHT:
{
kright = 1;
break;
}
case SDLK_LEFT:
{
kleft = 1;
break;
}
default:
break;
}
}
switch(keyevent.type)
{
case SDL_KEYUP:
switch(keyevent.key.keysym.sym)
{
case SDLK_RETURN:
{
kenter = 0;
break;
}
case SDLK_ESCAPE:
{
kescape = 0;
break;
}
case SDLK_a:
{
ka = 0;
break;
}
case SDLK_s:
{
ks = 0;
break;
}
case SDLK_UP:
{
kup = 0;
break;
}
case SDLK_DOWN:
{
kdown = 0;
break;
}
case SDLK_RIGHT:
{
kright = 0;
break;
}
case SDLK_LEFT:
{
kleft = 0;
break;
}
default:
break;
}
}
}
return 0;
}
I think your switch statements are broken.
Use this less confusing way
int pressed ()
{
SDL_Event event;
while(SDL_PollEvent(&event) )
{
if(event.type == SDLK_KEYDOWN)
{
switch(event.key.keysym.sym)
{
case SDLK_RETURN:
doStuff = 1
break;
default:
break;
}
}
if(event.type == SDLK_KEYUP)
{
switch(event.key.keysym.sym)
{
case SDLK_RETURN:
doStuff = 0;
break;
default:
break;
}
}
}
}
Also important:
SDL Tutorials: Practical Keyboard Input
Oh and avoid using global variables!
Your use of two switch statements is odd and confusing, you should probably fix that.
The issues you're seeing are likely due to keyboard repeat, see the SDL_EnableKeyRepeat() call for how to disable it.
don't use switch. I experienced same thing, but I resolved into using 'if' like this. Maybe switch has 'else if'.
if(event.type == SDL_KEYDOWN){
....
}
if(event.type == SDL_KEYUP){
....
}

Resources