I'm working on a PONG clone and just on the title screen. I have a class to work the title screen loop of the state machine and it uses very little, only a single sprite and a single true type font message. But when I call the function to render the message onto the SDL_Surface, it throws my program into whack. The error I receive is Unhandled exception at 0x6F4C2A9D (SDL_ttf.dll) in Pong.exe: 0xC0000005: Access violation reading location 0x00000000. Usually this means that I didn't initialize something or didn't define it in the class definition or something, but it all seems in order. So I'll post the code here in hopes that someone sees what's up with the render function or the bits surrounding it.
To be perfectly clear the exception is thrown on this line:
Title_Message = TTF_RenderText_Solid(font, "PONG", color);
//start code
/*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 Title : public GameState
{
private:
SDL_Surface *Title_Message = NULL;
SDL_Rect *clip;
Button Title_Button;
public:
void events();
void logic();
void render();
Title();
~Title();
};
/*FONTS*/
SDL_Color color = { 255, 255, 255 };
TTF_Font *font = NULL;
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);
//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("PIXELITE.ttf", 45);
if (font == NULL)
{
return false;
}
return true;
}
/*CLASS DEFINITIONS*/
Button::Button()
{
}
Title::Title()
{
Title_Message = TTF_RenderText_Solid(font, "PONG", color);
Title_Button.button_sprite = load_image("Start.png");
Title_Button.button.x = 200;
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;
while (SDL_PollEvent(&event))
{
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;
}
}
}
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);
}
Anybody got an idea? Thanks!
I will post my suggestions from the comments as an actual answer:
The trouble-causing line Title_Message = TTF_RenderText_Solid(font, "PONG", color); refers to the global variable font of type TTF_Font*. The line is also part of the constructor of the class Title.
main looks like this:
int main(int argc, char* args[])
{
//init SDL
init();
//load everything
load_files();
currentState = new Title;
//...
font is initialized to NULL at declaration, an actual object is assigned only in load_files() which is executed at the beginning of main before Title is the first time instantiated.
So load_files() has to assign a valid pointer to font, otherwise the next line in main will cause an access violation.
load_files() provides a return value depending on whether creating and assigning this object was successful. However main never checks for this value and thus it is not guaranteed that font is a valid pointer.
As knefcy pointed out the problem was a wrong filename in load_files().
Related
I've been trying to send a log in the console to make sure the player was created, but here is the results:
Here is the code along the problematic part:
SDL_Log("%s has %i hp out of %i", player1.name, player1.health, player1.maxhealth);
systemValues.h:
// Core Libraries and Headers
// - Global includes
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <string>
#include <typeinfo>
// Defines
#define GRAPHICS "../resources/"
struct Player
{
std::string sprite;
std::string name;
int level;
int size;
int health;
int maxhealth;
int magicpower; // Magic Points
int damages;
int armor;
int magic;
};
struct Enemy
{
std::string sprite;
std::string name;
int level;
int size;
int health;
int maxhealth;
int magicpower; // Magic Points
int damages;
int armor;
int magic;
};
Player createPlayer(std::string name);
Enemy createEnemy(std::string name, int level);
int damagePlayer(Player target, int damages);
int damageEnemy(Enemy target, int damages);
void close();
main.cpp:
// Local includes
// - Custom Headers
#include "systemValues.h"
// Screen constants and dimensions. (TODO: MOVE TO A PROPER HEADER FILE.)
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
// The window that is rendered to
SDL_Window* window = NULL;
// Quit being false means the application is running and did not request to quit
bool quit = false;
// Event handler
SDL_Event e;
// The surface contained by the window
SDL_Surface* screenSurface = NULL;
// Main function
int main(int argc, char* args[])
{
// Initializing SDL
if (SDL_Init(SDL_INIT_VIDEO)<0)
{
SDL_Log("SDL could not initialize! SDL_Error: %s\n", SDL_GetError());
}
else
{
// Creating the window
window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (window == NULL)
{
SDL_Log("The window could not be created! SDL_Error: %s\n", SDL_GetError());
}
else
{
Player player1 = createPlayer("Avarthar");
Enemy enemy1 = createEnemy("Blob", 1);
// Get the window surface
screenSurface = SDL_GetWindowSurface(window);
// While the application is running
while (!quit){
while (SDL_PollEvent(&e) != 0)
{
// User requests quit
if (e.type == SDL_QUIT)
{
quit = true;
}
// Checks for a key being pressed down
else if (e.type == SDL_KEYDOWN)
{
switch(e.key.keysym.sym)
{
// Checks if the key is F
case SDLK_f:
player1.health = damagePlayer(player1, 5);
SDL_Log("%s has %i hp out of %i", player1.name, player1.health, player1.maxhealth); // TODO - Remove the test line
break;
case SDLK_ESCAPE:
quit = true;
break;
}
}
}
// Test if player is created
if (player1.name == "Avarthar")
{
SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, (player1.health*-1+100*2), player1.health*2, 0));
}
else
{
SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 255, 255, 255));
}
// Update the surface
SDL_UpdateWindowSurface(window);
}
}
}
close();
// Return 0 to terminate the program
return 0;
}
// Creates a player with the given varables
Player createPlayer(std::string name)
{
Player player;
player.sprite = "";
player.name = name;
player.level = 1;
player.size = 32;
player.health = 100;
player.maxhealth = 100;
player.magicpower = 100;
player.damages = 1;
player.armor = 0;
player.magic = 1;
return player;
}
Enemy createEnemy(std::string name, int level)
{
Enemy enemy;
enemy.sprite = "";
enemy.name = name;
enemy.level = level;
enemy.size = 32;
enemy.health = 100;
enemy.maxhealth = 100;
enemy.magicpower = 100; // Magic Points
enemy.damages = 1;
enemy.armor = 0;
enemy.magic = 1;
return enemy;
}
// Deals damage to the target of type Player
int damagePlayer(Player target, int damages)
{
target.health -= damages;
// Checks whether the target is in the health range or not
if (target.health < 0)
target.health = 0;
if (target.health > target.maxhealth)
target.health = target.maxhealth;
return target.health;
}
// Deals damage to the target of type Enemy
int damageEnemy(Enemy target, int damages)
{
target.health -= damages;
// Checks whether the target is in the health range or not
if (target.health < 0)
target.health = 0;
if (target.health > target.maxhealth)
target.health = target.maxhealth;
return target.health;
}
void close()
{
// Wait 0.1 seconds
SDL_Delay(100);
// Clear the window from the memory
SDL_DestroyWindow(window);
// Quit the SDL subsystems
SDL_Quit();
}
What I tried so far:
I tried with &player1.name and it didn't change anything.
I also tried with only SDL_Log("%s", player1.name); and it instead wrote a single ? in the outputs.
The color changing according to the player1.health is working without any problem, but as soon as I try getting and sending the name in the SDL_Log it sends the outputs shown in the screenshot above.
SDL_Log cannot do std::string. Use player1.name.c_str(). This should have triggered a warning, check your compiler flags. – Quentin
Thanks for the quick answer and also thanks for noticing another problem that I had with my compiler ^^
the following program runs until SDL_GetRendererInfo is called (in function RenderInit()), and then stops working before SDL_GetError can do anything. replacing global_renderer with a null SDL_Renderer pointer doesn't cause a crash and the expected sdl error is gotten.
#include <iostream>
#include <list>
#include "SDL.h"
using namespace std;
//global variables:
bool run = true; // whether or not the program should be running
int global_window_width = 50; //width of window in tiles
int global_window_height = 35; //height of window in tiles
SDL_Window* global_window = NULL; //points to primary window
SDL_Renderer* global_renderer = NULL; //points to renderer for main window
SDL_RendererInfo* global_renderer_info = NULL; //points to info about above renderer once it's initialized
SDL_Texture* spritesheet = NULL; //holds the spritesheet
SDL_Event event; //for holding currently in-handling event
//function declarations:
int init(); //initialize SDL
int windowInit(); //initialize window
int renderInit(); //create renderers
int viewInit(); //manages all window, rendering, etc stuff
int loadSpritesheet(); //loads spritesheet
void cleanup(); //free up memory, etc
void dispatchEvent(); //main event handling
//function definitions:
int init()
{
if ( SDL_Init(SDL_INIT_EVERYTHING) !=0 )
{
cout << "could not initialize SDL. " << SDL_GetError();
return 1;
}
return 0;
}
int windowInit()
{
global_window = SDL_CreateWindow("roguelike",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
global_window_width * 10,
global_window_height * 10,
SDL_WINDOW_SHOWN);
if(global_window == NULL)
{
cout << "could create window. " << SDL_GetError();
return 1;
}
return 0;
}
int renderInit()
{
global_renderer = SDL_CreateRenderer(global_window,
-1,
SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_ACCELERATED );
if (global_renderer == NULL)
{
cout << "could not create renderer" << SDL_GetError();
return 1;
}
SDL_SetRenderDrawColor(global_renderer,0xFF,0xFF,0xFF,0xFF);
if (SDL_GetRendererInfo(global_renderer, global_renderer_info) != 0)
{
cout << "could not get renderer info" << SDL_GetError();
return 1;
}
return 0;
}
int viewInit()
{
if (windowInit() == 1)
{
return 1;
}
else if(renderInit() == 1)
{
return 1;
}
else if(loadSpritesheet() == 1)
{
return 1;
}
return 0;
}
int loadSpritesheet()
{
SDL_Surface* tempsurf = NULL; //using surface to get image initially, but since surfaces use cpu rendering we switch to textures immediately
tempsurf = SDL_LoadBMP("spritesheet.bmp"); //puts image in the surface
if (tempsurf == NULL)
{
cout << "failed to load spritesheet";
SDL_FreeSurface(tempsurf); //we don't need tempsurf anymore
return 1;
}
spritesheet = SDL_CreateTextureFromSurface(global_renderer, tempsurf);
if (spritesheet == NULL)
{
cout << "failed to create spritesheet texture";
SDL_FreeSurface(tempsurf); //we don't need tempsurf anymore
return 1;
}
SDL_FreeSurface(tempsurf); //we don't need tempsurf anymore
return 0;
}
void cleanup()
{
SDL_DestroyWindow(global_window);
global_window = NULL;
SDL_DestroyRenderer(global_renderer);
global_renderer = NULL;
SDL_DestroyTexture(spritesheet);
spritesheet = NULL;
global_renderer_info = NULL;
SDL_Quit();
}
void dispatchEvent()
{
SDL_PollEvent(&event); //stores current event information
switch(event.type)
{
case SDL_QUIT:
{
run = false;
break;
}
}
}
//classes:
class Layer // each layer holds visuals for a certain subset of what is to be displayed; environment, HUD, menu, etc.
{
Layer(){};
};
class Camera //renders environment, ui, etc in a series of layers
{
int width, height; //in tiles
SDL_Texture* texture_draw; //texture the camera draws to and sends to be displayed
list<Layer*> layer_list; //list of layers to be rendered, back --> front
public:
Camera(int x, int y, SDL_Renderer* renderer): width(x), height(y) //(width, height, renderer for camera to use)
{
texture_draw = SDL_CreateTexture(global_renderer,global_renderer_info->texture_formats[0] , SDL_TEXTUREACCESS_TARGET, 10*width, 10*height);
};
};
//main loop
int main(int argc, char *argv[]) //main function, needed by SDL
{
if(init() == 0)
{
if(viewInit() == 0)
{
while(run)
{
dispatchEvent();
}
}
}
cleanup();
return 0;
}
You're asking SDL_GetRendererInfo to write renderer info to location global_renderer_info points to, which is NULL. Writing to NULL causes segmentation fault. This should be expected behaviour.
Correct code should allocate memory for renderer info (preferably on stack or in global area) and write to that location, e.g.:
SDL_RendererInfo info = {0};
SDL_GetRendererInfo(global_renderer, &info);
// ... use info fields
Fixed by: instead of declaring global_renderer_info as a pointer to an SDL_RendererInfo, I instead declared the object directly and changed the rest of the code accordingly.
I'm making a basic program using SDL to render graphics. I have two classes that deal with Rendering:
A Texture class (that loads and renders the SDL_textures)
//Texture warpper class
class LTexture
{
private:
//The actual texture
SDL_Texture* mTexture;
//Image demensions
int mWidth;
int mHeight;
public:
//Initializes/Deallocates variables
LTexture();
~LTexture();
LTexture(const LTexture &rhs);
//Loads image at specified path
bool loadFromFile(std::string path);
//Deallocates texture
void free();
//Renders texture at given point
void render(int x, int y);
//Gets image dimensions
int getWidth();
int getHeight();
};
LTexture::LTexture()
{
//Initialize
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
LTexture::~LTexture()
{
//Deallocate
free();
}
LTexture::LTexture(const LTexture &rhs)
{
mTexture = rhs.mTexture;
mWidth = rhs.mWidth;
mHeight = rhs.mHeight;
}
bool LTexture::loadFromFile(std::string path)
{
//Get rid of preexisting texture
free();
//The final texture
SDL_Texture* newTexture = NULL;
//Load image at specified path
SDL_Surface* loadedSurface = IMG_Load(path.c_str());
if (loadedSurface == NULL)
{
printf("Unable to load image %s! SDL_image error: %s\n", path.c_str(), IMG_GetError());
}
else
{
//Create texture from surface pixels
newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
if (newTexture == NULL)
{
printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
}
else
{
//Get image dimensions
mWidth = loadedSurface->w;
mHeight = loadedSurface->h;
}
//Get rid of old loaded surface
SDL_FreeSurface(loadedSurface);
}
//Return success
mTexture = newTexture;
return mTexture != NULL;
}
void LTexture::free()
{
//Free Texture if it exists
if (mTexture != NULL)
{
SDL_DestroyTexture(mTexture);
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
}
void LTexture::render(int x, int y)
{
//Set rendering space and render to screen
SDL_Rect renderQuad = { x, y, mWidth, mHeight };
SDL_RenderCopy(gRenderer, mTexture, NULL, &renderQuad);
printf("Rendering...\n");
if (mTexture == NULL)
{
printf("No texture loaded!\n");
}
else
{
printf("Texture loaded! %s\n", mTexture);
}
}
int LTexture::getWidth()
{
return mWidth;
}
int LTexture::getHeight()
{
return mHeight;
}
And a Button class(That is supposed to make it easier to switch between the different textures associated with the states of the buttons. Each object is supposed to have 3 texture objects within the button).
Declaration:
//Class for buttons and chips
class Button
{
public:
//Initializes internal variables
Button();
//Handles mouse events
void handleEvent(SDL_Event* e);
//Render buttons
void render();
//Sets top left position
void setPosition(int x, int y);
//Gets image dimensions
void setWidth(int w);
void setHeight(int h);
//Set button status
void setButtonAction(buttonAction action);
//Get button status
buttonStatus getButtonStatus();
//Perform button action !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!FIXME::HAVEN'T DEFINED!!!!!!!!!!!!!!!!!!!!!!!!!!!!1!!
void activateButton(buttonAction action);
void setTextures(LTexture out, LTexture over, LTexture down);
private:
//Top left position
SDL_Point mPosition;
//Currently used global image
buttonStatus mButtonStatus;
//What happens if button is pressed
buttonAction mButtonAction;
//Width and height
int mWidth;
int mHeight;
//Textures
LTexture mOut;
LTexture mOver;
LTexture mDown;
};
Button::Button()
{
mPosition.x = 0;
mPosition.y = 0;
mWidth = 0;
mHeight = 0;
mButtonStatus = MOUSE_OUT;
}
void Button::setPosition(int x, int y)
{
mPosition.x = x;
mPosition.y = y;
}
void Button::handleEvent(SDL_Event* e)
{
bool mInside = true;
//If mouse event happened
if (e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEMOTION)
{
//Get mouse position
int x, y;
SDL_GetMouseState(&x, &y);
//Mouse is left of the button
if (x < mPosition.x)
{
mInside = false;
}
//Mouse is right of button
else if (x > mPosition.x + mWidth)
{
mInside = false;
}
//Mouse is above button
else if (y < mPosition.y)
{
mInside = false;
}
//Mouse is below button
else if (y > mPosition.y + mHeight)
{
mInside = false;
}
//Logic\\
//Mouse is outside of button
if (!mInside)
{
mButtonStatus = MOUSE_OUT;
}
//Mouse is inside of button
else
{
switch (e->type)
{
case SDL_MOUSEMOTION:
mButtonStatus = MOUSE_OVER;
break;
case SDL_MOUSEBUTTONDOWN:
mButtonStatus = MOUSE_BUTTON_DOWN;
break;
}
}
}
}
void Button::render()
{
switch (mButtonStatus)
{
case MOUSE_OUT:
mOut.render(mPosition.x, mPosition.y);
printf("Out rendered\n");
break;
case MOUSE_OVER:
mOver.render(mPosition.x, mPosition.y);
printf("Over rendered\n");
break;
case MOUSE_BUTTON_DOWN:
mDown.render(mPosition.x, mPosition.y);
printf("Down rendered\n");
break;
}
}
void Button::setWidth(int w)
{
mWidth = w;
}
void Button::setHeight(int h)
{
mHeight = h;
}
void Button::setButtonAction(buttonAction action)
{
mButtonAction = action;
}
buttonStatus Button::getButtonStatus()
{
return mButtonStatus;
}
void Button::setTextures(LTexture out, LTexture over, LTexture down)
{
mOut = out;
mOver = over;
mDown = down;
}
Unfortunately, when I try to use a copy constructor to pass the SDL_Texture value from the original Texture to the buttons Private texture, it doesn't pass any value, but still thinks that it's not "NULL"
There's two problems with this code that I can see.
The first is that in the LTexture copy constructor, you only copy the pointer to the SDL_Texture. This is called a shallow copy. Then, in the destructor of LTexture, you call free(), which deletes the SDL_Texture. This is bad, because it means that any duplicates of the LTexture now have pointers to textures that have been deleted, so they will no longer work. The best two solutions here are either to use a c++11 class called shared_ptr to store the texture, which will prevent it from being deleted, or actually make a copy of the texture and point to the new one (a deep copy). For testing, you could try just commenting out the call to SDL_DestroyTexture in free().
Additionally, you forgot to implement the assignment operator. Currently it behaves just like the copy constructor (since all you do is a shallow copy), but if you make it a deep copy, you'll have to implement that as well.
I'm currently having a problem with my pong game. I'm trying to smooth out the player movement (so it wont stutter so much when is moves, and no delay after first keypress)
This is my code so far, problem is while I've got the movement smoother the _paddle move too fast! and I don't now how to make it move slower!
Is there a way i can make the _paddle move slower or did i write the code wrong?
maingame.h
#pragma once
#include <iostream>
#include <SDL/SDL.h>
#include <string>
#include "maingame.h"
class maingame
{
public:
maingame();
~maingame();
//loads pictures
bool loadMedia(std::string path);
//init the system
void init();
//runs the game
void run();
//THE EPIC GAMELOOP
void gameloop();
//draw the screen
void draw();
void UserInput();
private:
//window
SDL_Window* _window;
//redenderer
SDL_Renderer* _rend;
//the screens surface
SDL_Surface* _screensurface;
//player, ai and the ball
SDL_Rect _paddle;
SDL_Rect _ai;
SDL_Rect _ball;
//checks if you pressed down the W or S button
bool keydown_w = false;
bool keydown_s = false;
//Event for the pall stuff
SDL_Event e;
};
maingame.c
#include "maingame.h"
/*
PONG V0.2
Black background - CHECK
paddle appear on screen - CHECK
other paddle appear on screen - CHECK
ball appear on screen - CHECK
move player paddle - CHECK
impossible to move outside of map - CHECK
movement smoother -
make ball go around -
collison with paddles -
keep scores -
show scores -
make a 2nd player chooseable -
*/
//screen width and height
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 768;
maingame::maingame()
{
_window = nullptr;
_rend = nullptr;
}
maingame::~maingame()
{
}
void maingame::init()
{
SDL_Init(SDL_INIT_EVERYTHING);
}
void maingame::run()
{
init();
//creating a windows
_window = SDL_CreateWindow("PONG", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
//create the render
_rend = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
//set out the player
_paddle.x = 100;
_paddle.y = (SCREEN_HEIGHT / 2) - 100;
_paddle.w = 20;
_paddle.h = 200;
//set out the ai
_ai.x = SCREEN_WIDTH - 100;
_ai.y = (SCREEN_HEIGHT / 2) - 100;
_ai.w = 20;
_ai.h = 200;
//set out the ball
_ball.x = SCREEN_WIDTH / 2 - 20;
_ball.y = (SCREEN_HEIGHT / 2) - 20;
_ball.w = 20;
_ball.h = 20;
draw();
gameloop();
}
void maingame::draw()
{
//make the render be black
SDL_SetRenderDrawColor(_rend, 0, 0, 0, 0);
//Clear the render with the color we set with SDL_SetRenderDrawColor, in this case black
SDL_RenderClear(_rend);
//make the next things we will render white
SDL_SetRenderDrawColor(_rend, 255, 255, 255, 0);
//make the paddle, ai and ball the color of SDL_SetRenderDrawColor, which is whites in this case
SDL_RenderFillRect(_rend, &_paddle);
SDL_RenderFillRect(_rend, &_ai);
SDL_RenderFillRect(_rend, &_ball);
//SDL_RenderDrawRect(_rend, &_paddle);
//Present the render, draw it to the screen
SDL_RenderPresent(_rend);
}
bool maingame::loadMedia(std::string path)
{
//Loading success flag
bool success = true;
SDL_Surface* pic;
//Load splash image
pic = SDL_LoadBMP(path.c_str());
if (pic == NULL)
{
printf("Unable to load image %s! SDL Error: %s\n", "02_getting_an_image_on_the_screen/hello_world.bmp", SDL_GetError());
success = false;
}
return success;
}
void maingame::gameloop()
{
const Uint8 *keys = SDL_GetKeyboardState(NULL);
bool keydown_w = false;
bool keydown_s = false;
while (true)
{
while (SDL_PollEvent(&e) != 0)
{
//pressed the X, quit the program
if (e.type == SDL_QUIT)
{
exit(1);
}
UserInput();
}
UserInput();
draw();
}
}
void maingame::UserInput()
{
float lol = 0;
//Pressed a key!
if (e.type == SDL_KEYDOWN)
{
//pressed W, move the player
if (e.key.keysym.sym == SDLK_w)
{
keydown_w = true;
}
//pressed S, move the player
else if (e.key.keysym.sym == SDLK_s)
{
keydown_s = true;
}
}
if (e.type == SDL_KEYUP)
{
std::cout << keydown_w << std::endl;
if (e.key.keysym.sym == SDLK_w)
keydown_w = false;
if (e.key.keysym.sym == SDLK_s)
keydown_s = false;
}
if (keydown_w)
{
if (_paddle.y > 1)
{
_paddle.y -= 1;
}
std::cout << keydown_w << std::endl;
}
if (keydown_s)
{
if (_paddle.y < SCREEN_HEIGHT - _paddle.h)
{
_paddle.y += 1;
}
}
}
It maybe bad programming practice to do so but the only way I would see getting around your problem is to add an SDL_Delay() in your gameloop function. I would suggest you use it as a temporary fix until you find an alternative solution. Hope this helps.
I'm working on the motion for my paddles in my PONG clone and the program doesn't want to register SDL_KEYUP/DOWN sometimes. I think I have an idea of why this is but now how to fix it. So the way my game is looped currently I am using a state machine in my main() function that uses a Game class state which manages two paddles and a ball all of which are three different class instances. In side my Game class I run the events/logic/render from all three classes like so
void Game::events()
{
while (SDL_PollEvent(&event))
{
PlayerOne.events();
PlayerTwo.events();
Game_Ball.events();
}
}
void Game::logic()
{
PlayerOne.logic(delta.get_ticks());
PlayerTwo.logic(delta.get_ticks());
Game_Ball.logic();
}
void Game::render()
{
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
PlayerOne.render();
PlayerTwo.render();
Game_Ball.render();
}
And the paddle class movement is managed like so (as fixed in the last question I had(:
void Paddle::events()
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if (event.type == SDL_KEYDOWN)
{
if (player == 1)
{
if (keystates[SDLK_o] == 1)
{
yVel -= 100;
}
if (keystates[SDLK_k] == 1)
{
yVel += 100;
}
}
else
{
if (keystates[SDLK_w] == 1)
{
yVel -= 100;
}
if (keystates[SDLK_s] == 1)
{
yVel += 100;
}
}
}
if (event.type == SDL_KEYUP)
{
if (player == 1)
{
if (keystates[SDLK_o] == 0)
{
yVel += 100;
}
if (keystates[SDLK_k] == 0)
{
yVel -= 100;
}
}
else
{
if (keystates[SDLK_w] == 0)
{
yVel += 100;
}
if (keystates[SDLK_s] == 0)
{
yVel -= 100;
}
}
}
if (event.type == SDL_QUIT)
{
quit = true;
}
}
The problem here is that sometimes, if not most of the time, the keys don't react to move the player paddles up and down. but when they do it's less than half the time. I think this is because there is only a 1/3 chance that the program polls when a key is released or pressed, sometimes resulting in no change in the position. Basically, if PLAYERONE hits a key while Game is running PlayerTwo.events() it won't register the key press/release. Could this be a problem in the way I'm implementing the game loop in my main function?
int main(int argc, char* args[])
{
//init SDL
if (init() == false)
{
return 1;
}
//load everything
if (load_files() == false)
{
return 1;
}
delta.start();
currentState = new Game;
while (quit == false)
{
//handle state events
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;
}
}
clean_up();
return 0;
}
And here are the definitions and constructors
class Paddle
{
public:
int player;
SDL_Surface *sprite = NULL;
float x, y, w, h;
float yVel;
SDL_Rect *clip;
void events();
void logic(Uint32 deltaTicks);
void render();
Paddle();
~Paddle();
};
class Game : public GameState
{
private:
int server;
TTF_Font *score = NULL;
Paddle PlayerOne;
Paddle PlayerTwo;
Ball Game_Ball;
public:
SDL_Rect clip[2];
void events();
void logic();
void render();
Game();
~Game();
};
Game::Game()
{
//RESOURSES
PlayerOne.sprite = load_image("Game_sprite.png");
PlayerTwo.sprite = load_image("Game_sprite.png");
clip[0].x = 0;
clip[0].y = 0;
clip[0].w = 20;
clip[0].h = 20;
clip[1].x = 0;
clip[1].y = 20;
clip[1].w = 20;
clip[1].h = 100;
//PLAYER ONE
PlayerOne.x = 420;
PlayerOne.y = 100;
PlayerOne.w = 60;
PlayerOne.h = 100;
PlayerOne.clip = &clip[1];
PlayerOne.yVel = 0;
PlayerOne.player = 1;
//PLAYER TWO
PlayerTwo.x = 60;
PlayerTwo.y = 100;
PlayerTwo.w = 60;
PlayerTwo.h = 100;
PlayerTwo.clip = &clip[1];
PlayerTwo.yVel = 0;
PlayerTwo.player = 2;
//BALL
Game_Ball.Ball_Bound.x = 190;
Game_Ball.Ball_Bound.y = 190;
Game_Ball.Ball_Bound.w = 60;
Game_Ball.Ball_Bound.h = 60;
Game_Ball.clip = &clip[0];
}
Because that looks pretty clean to me. Other than another game loop implementation, what could I do to fix the keys not registering due to being in a different function? Or is it a loop implementation problem? If it is a loop problem, can you be through or include a reference as to how to fix it?
I don't see anything wrong in this code. However, the very possible cause might be simply limitation of how many keys are pressed on keyboard. Try using shift, ctrl and alt, keys for controls, they usually bypass the limitation.