I'm making a two player pong clone and my problem is that when the second player presses both of his/her buttons at once, the first player locks up and cannot move, even though this does not happen when player one does this. This is pretty much the last kink I have in this clone so I'd like to handle it so I can move on to something new. The code that takes the key strokes looks like this:
void Paddle::events()
{
//press
if (player == 1)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if (keystates[SDLK_o] == 1)
{
yVel = -200;
return;
}
if (keystates[SDLK_k] == 1)
{
yVel = 200;
return;
}
}
if (player == 2)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if (keystates[SDLK_w] == 1)
{
yVel = -200;
return;
}
if (keystates[SDLK_s] == 1)
{
yVel = 200;
return;
}
}
//release
if (player == 1)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if ((keystates[SDLK_o] == 0) && (keystates[SDLK_k] == 0))
{
yVel = 0;
return;
}
}
if (player == 2)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if ((keystates[SDLK_w] == 0) && (keystates[SDLK_s] == 0))
{
yVel = 0;
return;
}
}
if (event.type == SDL_QUIT)
{
quit = true;
}
}
I've stewed over it for quite some time, but don't see how it could be doing that. Could it be hardware related?
And as a note, event polling occurs in my main loop, which is supported by a state machine, so that's why you don't see the classic while (SDL_EventPoll (&event))
in there.
I fail to understand how implementing Polls for each player would work withing proper game loop writing, I've been taught only to Poll in the main loop and not in classes or methods. But either way, here is the entirety of the code:
#include "SDL.h"
#include "SDL_image.h"
#include "SDL_ttf.h"
#include "SDL_mixer.h"
#include <iostream>
#include <string>
#include <sstream>
#include <stdlib.h>
/*CLASSES*/
class GameState
{
public:
virtual void events() = 0;
virtual void logic() = 0;
virtual void render() = 0;
virtual ~GameState(){};
};
class Button
{
public:
SDL_Rect button_clip[2];
SDL_Rect button;
SDL_Surface *button_sprite = NULL;
Button();
};
class Paddle
{
private:
public:
int player;
float x, y, w, h;
float yVel;
SDL_Surface *sprite = NULL;
SDL_Rect clip;
SDL_Rect *cliping;
void events();
void logic(Uint32 deltaticks);
void render();
Paddle();
~Paddle();
};
class Ball
{
private:
SDL_Rect clip[1];
public:
float temp_x;
float temp_y;
bool win;
SDL_Surface *win_message;
int score_P1;
int score_P2;
Paddle *paddle_pointer1;
Paddle *paddle_pointer2;
SDL_Rect *collision;
float x, y, w, h;
float xVel, yVel;
SDL_Surface *sprite = NULL;
SDL_Rect *cliping;
void events();
void logic(Uint32 deltaticks);
void render();
Ball();
~Ball();
};
class Game : public GameState
{
public:
SDL_Surface *score_message = NULL;
int *score_P1;
int *score_P2;
Paddle PlayerOne;
Paddle PlayerTwo;
Ball Game_Ball;
SDL_Rect clip[1];
void events();
void logic();
void render();
Game();
~Game();
};
class Title : public GameState
{
private:
SDL_Surface *Title_Message = NULL;
SDL_Rect *clip;
Button Title_Button;
public:
void events();
void logic();
void render();
Title();
~Title();
};
class Timer
{
private:
//The clock time when the timer started
int startTicks;
//The ticks stored when the timer was paused
int pausedTicks;
//The timer status
bool paused;
bool started;
public:
//Initializes variables
Timer();
//The various clock actions
void start();
void stop();
void pause();
void unpause();
//Gets the timer's time
int get_ticks();
//Checks the status of the timer
bool is_started();
bool is_paused();
};
/*CONSTANTS AND GLBALS*/
const int SCREEN_WIDTH = 700;
const int SCREEN_HEIGHT = 500;
const int SCREEN_BPP = 32;
const int FPS = 100;
/*VARS*/
bool quit = false;
/*GAMESTATES*/
enum States
{
S_NULL,
TITLE,
GAME,
WIN,
EXIT,
};
/*SURFACES*/
SDL_Surface *screen = NULL;
/*FONTS*/
SDL_Color color = { 255, 255, 255 };
TTF_Font *font = NULL;
//STATE VARS
GameState *currentState = NULL;
int stateID = NULL;
int nextState = NULL;
//SDL event
SDL_Event event;
//timer
Timer delta;
//key
Uint8 *keystates = SDL_GetKeyState(NULL);
/*FUNCTIONS*/
SDL_Surface *load_image(std::string filename)
{
//loaded image
SDL_Surface* loadedImage = NULL;
//optimized surface
SDL_Surface* optimizedImage = NULL;
//load image
loadedImage = IMG_Load(filename.c_str());
//if image loaded
if (loadedImage != NULL)
{
//Create optimized image
optimizedImage = SDL_DisplayFormat(loadedImage);
//free old image
SDL_FreeSurface(loadedImage);
//if optimized
if (optimizedImage != NULL)
{
//map color key
Uint32 colorkey = SDL_MapRGB(optimizedImage->format, 0, 0, 0);
//set all pixles of color 0,0,0 to be transparent
SDL_SetColorKey(optimizedImage, SDL_SRCCOLORKEY, colorkey);
}
}
return optimizedImage;
}
//apply image
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL)
{
//temp rect
SDL_Rect offset;
//offsets
offset.x = x;
offset.y = y;
//blit
SDL_BlitSurface(source, clip, destination, &offset);
}
//initiate SDL etc
bool init()
{
//initialize all SDL subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return false;
}
//set up screen
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);
if (screen == NULL)
{
return false;
}
//check screen
if (screen == NULL)
{
return false;
}
//init TTF
if (TTF_Init() == -1)
{
return false;
}
//set window caption
SDL_WM_SetCaption("PONG", NULL);
//if evetything worked
return true;
}
//load files
bool load_files()
{
font = TTF_OpenFont("SF Pixelate.ttf", 45);
if (font == NULL)
{
return false;
}
return true;
}
//quit
void clean_up()
{
//delete game state
//free image
//quit ttf
TTF_CloseFont(font);
TTF_Quit();
//quit SDL
SDL_Quit();
}
// STATE MACHINE FUNCTIONS //
void set_next_state(int newState)
{
if (nextState != EXIT)
{
nextState = newState;
}
}
void change_state()
{
if (nextState != S_NULL)
{
//change state
switch (nextState)
{
case GAME:
currentState = new Game();
break;
}
//change state
stateID = nextState;
//null nextState
nextState = S_NULL;
}
}
/*CLASS DEFINITIONS*/
Timer::Timer()
{
//Initialize the variables
startTicks = 0;
pausedTicks = 0;
paused = false;
started = false;
}
void Timer::start()
{
//Start the timer
started = true;
//Unpause the timer
paused = false;
//Get the current clock time
startTicks = SDL_GetTicks();
}
void Timer::stop()
{
//Stop the timer
started = false;
//Unpause the timer
paused = false;
}
void Timer::pause()
{
//If the timer is running and isn't already paused
if ((started == true) && (paused == false))
{
//Pause the timer
paused = true;
//Calculate the paused ticks
pausedTicks = SDL_GetTicks() - startTicks;
}
}
void Timer::unpause()
{
//If the timer is paused
if (paused == true)
{
//Unpause the timer
paused = false;
//Reset the starting ticks
startTicks = SDL_GetTicks() - pausedTicks;
//Reset the paused ticks
pausedTicks = 0;
}
}
int Timer::get_ticks()
{
//If the timer is running
if (started == true)
{
//If the timer is paused
if (paused == true)
{
//Return the number of ticks when the timer was paused
return pausedTicks;
}
else
{
//Return the current time minus the start time
return SDL_GetTicks() - startTicks;
}
}
//If the timer isn't running
return 0;
}
bool Timer::is_started()
{
return started;
}
bool Timer::is_paused()
{
return paused;
}
Button::Button()
{
}
Title::Title()
{
//text/sprites
Title_Message = TTF_RenderText_Solid(font, "PONG", color);
Title_Button.button_sprite = load_image("Start.png");
//region for button
Title_Button.button.x = 300;
Title_Button.button.y = 350;
Title_Button.button.w = 100;
Title_Button.button.h = 50;
//clips not hover
Title_Button.button_clip[0].x = 0;
Title_Button.button_clip[0].y = 0;
Title_Button.button_clip[0].w = 100;
Title_Button.button_clip[0].h = 50;
//clips hover
Title_Button.button_clip[1].x = 0;
Title_Button.button_clip[1].y = 50;
Title_Button.button_clip[1].w = 100;
Title_Button.button_clip[1].h = 50;
}
Title::~Title()
{
SDL_FreeSurface(Title_Message);
SDL_FreeSurface(Title_Button.button_sprite);
}
void Title::events()
{
int x = 0;
int y = 0;
if (event.type == SDL_MOUSEMOTION)
{
x = event.motion.x;
y = event.motion.y;
if ((x > Title_Button.button.x) && (x < (Title_Button.button.x + Title_Button.button.w)) && (y > Title_Button.button.y) && (y < (Title_Button.button.y + Title_Button.button.h)))
{
clip = &Title_Button.button_clip[1];
}
else
{
clip = &Title_Button.button_clip[0];
}
}
if (event.type == SDL_QUIT)
{
quit = true;
}
if (event.type == SDL_MOUSEBUTTONDOWN)
{
if (event.button.button == SDL_BUTTON_LEFT)
{
x = event.motion.x;
y = event.motion.y;
if ((x > Title_Button.button.x) && (x < (Title_Button.button.x + Title_Button.button.w)) && (y > Title_Button.button.y) && (y < (Title_Button.button.y + Title_Button.button.h)))
{
set_next_state(GAME);
}
}
}
}
void Title::logic()
{
}
void Title::render()
{
apply_surface(Title_Button.button.x, Title_Button.button.y, Title_Button.button_sprite, screen, clip);
apply_surface((SCREEN_WIDTH - Title_Message->w) / 2, 100, Title_Message, screen);
}
Paddle::Paddle()
{
}
Paddle::~Paddle()
{
}
void Paddle::events()
{
//press
if (player == 1)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if (keystates[SDLK_o] == 1)
{
yVel = -200;
return;
}
if (keystates[SDLK_k] == 1)
{
yVel = 200;
return;
}
}
if (player == 2)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if (keystates[SDLK_w] == 1)
{
yVel = -200;
return;
}
if (keystates[SDLK_s] == 1)
{
yVel = 200;
return;
}
}
//release
if (player == 1)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if ((keystates[SDLK_o] == 0) && (keystates[SDLK_k] == 0))
{
yVel = 0;
return;
}
}
if (player == 2)
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if ((keystates[SDLK_w] == 0) && (keystates[SDLK_s] == 0))
{
yVel = 0;
return;
}
}
if (event.type == SDL_QUIT)
{
quit = true;
}
}
void Paddle::logic(Uint32 deltaticks)
{
y += yVel * (deltaticks / 1000.f);
if (y < 0)
{
y = 0;
}
else if (y > SCREEN_HEIGHT - h)
{
y = SCREEN_HEIGHT - h;
}
}
void Paddle::render()
{
apply_surface((int)x, (int)y, sprite, screen, cliping);
}
Ball::Ball()
{
sprite = load_image("Game_sprite.png");
clip[0].x = 0;
clip[0].y = 0;
clip[0].w = 20;
clip[0].h = 20;
cliping = &clip[0];
win = false;
x = 340;
y = 240;
w = 20;
h = 20;
yVel = 0;
xVel = 200;
}
Ball::~Ball()
{
}
void Ball::events()
{
if (event.type == SDL_QUIT)
{
quit = true;
}
}
void Ball::logic(Uint32 deltaticks)
{
temp_x = x + xVel * (deltaticks / 1000.f);
temp_y = y + yVel * (deltaticks / 1000.f);
if (x < 0)
{
score_P1 += 1;
x = 340;
y = 240;
yVel = 200;
xVel = -170;
SDL_Delay(1000);
return;
}
if (x > SCREEN_WIDTH - w)
{
score_P2 += 1;
x = 340;
y = 240;
yVel = 200;
xVel = 170;
SDL_Delay(1000);
return;
}
if ((temp_y < 0) || (y + w > SCREEN_HEIGHT))
{
yVel = -yVel;
y += yVel * (deltaticks / 1000.f);
return;
}
//standard collisions
//P1
if (((temp_x <= (paddle_pointer1->x + paddle_pointer1->w)) && ((temp_x + w) >= paddle_pointer1->x)) && (((temp_y + h) >= paddle_pointer1->y) && (temp_y <= (paddle_pointer1->y + paddle_pointer1->h))))
{
if (xVel > 0)
{
xVel += 30;
}
else
{
xVel -= 30;
}
if (yVel >= 0)
{
if (paddle_pointer2->yVel > 0)
{
yVel += 50;
xVel = -xVel;
}
else
{
yVel += 50;
yVel = -yVel;
xVel = -xVel;
}
x -= paddle_pointer2->yVel * (deltaticks / 1000.f);
return;
}
if (yVel <= 0)
{
if (paddle_pointer2->yVel > 0)
{
yVel += 50;
yVel = -yVel;
xVel = -xVel;
}
else
{
yVel += 50;
xVel = -xVel;
}
x += paddle_pointer2->yVel * (deltaticks / 1000.f);
return;
}
}
//P2
if (((temp_x <= (paddle_pointer2->x + paddle_pointer2->w)) && ((temp_x + w) >= paddle_pointer2->x)) && (((temp_y + h) >= paddle_pointer2->y) && (temp_y <= (paddle_pointer2->y + paddle_pointer2->h))))
{
if (xVel > 0)
{
xVel += 30;
}
else
{
xVel -= 30;
}
if (yVel >= 0)
{
if (paddle_pointer2->yVel > 0)
{
yVel += 50;
xVel = -xVel;
}
else
{
yVel += 50;
yVel = -yVel;
xVel = -xVel;
}
x -= paddle_pointer2->yVel * (deltaticks / 1000.f);
return;
}
if (yVel <= 0)
{
if (paddle_pointer2->yVel > 0)
{
yVel += 50;
yVel = -yVel;
xVel = -xVel;
}
else
{
yVel += 50;
xVel = -xVel;
}
x += paddle_pointer2->yVel * (deltaticks / 1000.f);
return;
}
}
y += yVel * (deltaticks / 1000.f);
x += xVel * (deltaticks / 1000.f);
}
void Ball::render()
{
if (score_P1 == 11)
{
win_message = TTF_RenderText_Solid(font, "Player One Wins", color);
apply_surface((SCREEN_WIDTH - win_message->w) / 2, (SCREEN_HEIGHT - win_message->h) / 2, win_message, screen);
win = true;
}
else if (score_P2 == 11)
{
win_message = TTF_RenderText_Solid(font, "Player Two Wins", color);
apply_surface((SCREEN_WIDTH - win_message->w) / 2, (SCREEN_HEIGHT - win_message->h) / 2, win_message, screen);
win = true;
}
else
{
apply_surface((int)x, (int)y, sprite, screen, cliping);
}
}
Game::Game()
{
//clip sprite
//paddle
clip[0].x = 0;
clip[0].y = 20;
clip[0].w = 20;
clip[0].h = 100;
//ball
clip[1].x = 0;
clip[1].y = 0;
clip[1].w = 20;
clip[1].h = 20;
//player one
PlayerOne.sprite = load_image("Game_sprite.png");
PlayerOne.clip.x = 0;
PlayerOne.clip.y = 20;
PlayerOne.clip.w = 20;
PlayerOne.clip.h = 100;
PlayerOne.x = 680;
PlayerOne.y = 200;
PlayerOne.w = 20;
PlayerOne.h = 100;
PlayerOne.cliping = &clip[0];
PlayerOne.yVel = 0;
PlayerOne.player = 1;
//player two
PlayerTwo.sprite = load_image("Game_sprite.png");
PlayerTwo.clip.x = 0;
PlayerTwo.clip.y = 20;
PlayerTwo.clip.w = 20;
PlayerTwo.clip.h = 100;
PlayerTwo.x = 0;
PlayerTwo.y = 200;
PlayerTwo.w = 20;
PlayerTwo.h = 100;
PlayerTwo.cliping = &clip[0];
PlayerTwo.yVel = 0;
PlayerTwo.player = 2;
//pointers
Game_Ball.paddle_pointer1 = &PlayerOne;
Game_Ball.paddle_pointer2 = &PlayerTwo;
score_P1 = &Game_Ball.score_P1;
score_P2 = &Game_Ball.score_P2;
}
Game::~Game()
{
SDL_FreeSurface(PlayerOne.sprite);
SDL_FreeSurface(PlayerTwo.sprite);
}
void Game::events()
{
Game_Ball.events();
PlayerOne.events();
PlayerTwo.events();
}
void Game::logic()
{
Game_Ball.logic(delta.get_ticks());
PlayerOne.logic(delta.get_ticks());
PlayerTwo.logic(delta.get_ticks());
}
void Game::render()
{
std::stringstream score_current;
score_current << *score_P2 << " : " << *score_P1;
score_message = TTF_RenderText_Solid(font, score_current.str().c_str(), color);
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
apply_surface((SCREEN_WIDTH - score_message->w) / 2, 0, score_message, screen);
if (Game_Ball.win == false)
{
Game_Ball.render();
PlayerOne.render();
PlayerTwo.render();
}
if (Game_Ball.win == true)
{
PlayerOne.render();
PlayerTwo.render();
SDL_Flip(screen);
SDL_Delay(2000);
set_next_state(TITLE);
}
}
/*MAIIIIIIN*/
int main(int argc, char* args[])
{
Timer fps;
//init SDL
if (init() == false)
{
return 1;
}
//load everything
if (load_files() == false)
{
return 1;
}
delta.start();
currentState = new Title();
while (quit == false)
{
//Keystates
Uint8 *keystates = SDL_GetKeyState(NULL);
//handle state events
while (SDL_PollEvent(&event))
{
currentState->events();
}
// do state logic
currentState->logic();
//timer reset
delta.start();
//change state if needed
change_state();
//render state
currentState->render();
if (SDL_Flip(screen) == -1)
{
return 1;
}
if (fps.get_ticks() < (1000 / FPS))
{
SDL_Delay((1000 / FPS) - fps.get_ticks());
}
}
clean_up();
return 0;
}
Hopefully this makes it clear the way I'm implementing the event handling.
events()
you should haveplayer1Events()
andplayer2Events()
with an SDL_EventPoll() call for each. I am almost certain this is the problem \$\endgroup\$