I am making a pong clone in C++/SDL2. To take keyboard input from multiple keys, I would like to use SDL_GetKeyboardState(NULL) with a Uint8. However, it isn't working as expected.
Expected behavior: pong paddles move up and down with each keypress.
Actual behavior: keys are not registered.
Here is my code (note that variables written in uppercase letters are from variables.h):
// g++ main.cpp `pkg-config sdl2 SDL2_ttf --cflags --libs`
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <iostream> // For io
#include <time.h> // Rand seeder
const bool debug = true;
// Basic stuff needed for SDL2
const int WINDOW_WIDTH = 800;
const int WINDOW_HEIGHT = 600;
const int WINDOW_CENTER_X = WINDOW_WIDTH / 2;
const int WINDOW_CENTER_Y = WINDOW_HEIGHT / 2;
const char* WINDOW_TITLE = "Pong";
// Define the location of the paddles in the bitmap
const int PADDLE_BITMAP_X = 0;
const int PADDLE_BITMAP_Y = 0;
const int BALL_BITMAP_X = 100;
const int BALL_BITMAP_Y = 0;
// Positions of player 1 and player 2
const int PADDLE1_X = 50;
const int PADDLE2_X = WINDOW_WIDTH - 75;
// Paddle width and height
const int PADDLE_WIDTH = 20;
const int PADDLE_HEIGHT = 100;
// Speed of both players
const int PLAYER_SPEED = 0;
// Ball Speed -- this will not be constant
// The Ball Modifier will modify the speed based on the place it lands on the paddle
// Ball speed will never go under 10, and will accelerate over time
// Ball diameter
const int BALL_SPEED = 100;
const int BALL_MODIFIER = 5;
const int BALL_DIAMETER = 10;
const int BALL_CENTER_X = WINDOW_CENTER_X - BALL_DIAMETER;
const int BALL_CENTER_Y = WINDOW_CENTER_Y - BALL_DIAMETER;
const int LANG = 3;
const int LANG_ENGLISH = 3;
const int LANG_GERMAN = 4;
// Game Entity data structure
struct GameObject
{
SDL_Rect ScreenLocation;
SDL_Rect BitmapLocation;
int SpeedX;
int SpeedY;
};
// SDL stuff
SDL_Window* gWindow = NULL;
SDL_Renderer* gWindowRenderer;
SDL_Rect gScreen;
SDL_Event e;
// Game objects
GameObject gPlayer1; // Paddle 1
GameObject gPlayer2; // Paddle 2
GameObject gBall; // Ball
// Scores. I feel like it's better to leave them uninitialized.
int gPlayer1Score;
int gPlayer2Score;
void Render();
bool init();
bool GameQuit;
void Quit();
int main( int argc, char* argv[] )
{
// const Uint8* keyStates = SDL_GetKeyboardState(NULL);
if( init() == false )
{
std::cout << "Init failed. Bye bye!\n";
return 1;
}
while( !GameQuit )
{
const Uint8* keyStates = SDL_GetKeyboardState( NULL );
while( SDL_PollEvent( &e ) )
{
if( e.type == SDL_QUIT )
GameQuit = true;
if( e.type == SDL_KEYDOWN )
{
switch( e.key.keysym.scancode )
{
case SDL_SCANCODE_ESCAPE:
GameQuit = true;
break;
}
}
}
if( keyStates[ SDL_SCANCODE_UP ] )
{
gPlayer1.ScreenLocation.y -= PLAYER_SPEED;
}
if( keyStates[ SDL_SCANCODE_DOWN ] )
{
gPlayer1.ScreenLocation.y += PLAYER_SPEED;
}
if( keyStates[ SDL_SCANCODE_W ] )
{
gPlayer2.ScreenLocation.y -= PLAYER_SPEED;
}
if( keyStates[ SDL_SCANCODE_S ] )
{
gPlayer2.ScreenLocation.y += PLAYER_SPEED;
}
Render();
}
Quit();
return 0;
}
bool init()
{
bool success = true;
if( SDL_Init( SDL_INIT_VIDEO < 0 ) )
{
success = false;
return success;
}
if( TTF_Init() < 0 )
{
success = false;
return success;
}
srand( time( NULL ) ); // Seed rng
// From SDL_Rect.h
// 64 typedef struct SDL_Rect
// 65 {
// 66 int x, y;
// 67 int w, h;
// 68 } SDL_Rect;
GameQuit = false;
gPlayer1.ScreenLocation.y = ( ( WINDOW_HEIGHT / 2 ) - ( PADDLE_HEIGHT / 2 ) );
gPlayer1.ScreenLocation.x = PADDLE1_X;
gPlayer1.ScreenLocation.w = PADDLE_WIDTH;
gPlayer1.ScreenLocation.h = PADDLE_HEIGHT;
gPlayer2.ScreenLocation.y = ( ( WINDOW_HEIGHT / 2 ) - ( PADDLE_HEIGHT / 2 ) );
gPlayer2.ScreenLocation.x = PADDLE2_X;
gPlayer2.ScreenLocation.w = PADDLE_WIDTH;
gPlayer2.ScreenLocation.h = PADDLE_HEIGHT;
gBall.ScreenLocation.x = BALL_CENTER_X;
gBall.ScreenLocation.y = BALL_CENTER_Y;
gBall.ScreenLocation.w = BALL_DIAMETER;
gBall.ScreenLocation.h = BALL_DIAMETER;
gPlayer1.BitmapLocation.x = PADDLE_BITMAP_X;
gPlayer1.BitmapLocation.y = PADDLE_BITMAP_Y;
gPlayer1.BitmapLocation.w = PADDLE_WIDTH;
gPlayer1.BitmapLocation.h = PADDLE_HEIGHT;
gPlayer2.BitmapLocation.x = PADDLE_BITMAP_X;
gPlayer2.BitmapLocation.y = PADDLE_BITMAP_Y;
gPlayer2.BitmapLocation.w = PADDLE_WIDTH;
gPlayer2.BitmapLocation.h = PADDLE_HEIGHT;
gBall.BitmapLocation.x = BALL_BITMAP_X;
gBall.BitmapLocation.y = BALL_BITMAP_Y;
gBall.BitmapLocation.w = BALL_DIAMETER;
gBall.BitmapLocation.h = BALL_DIAMETER;
gPlayer1.SpeedY = PLAYER_SPEED;
gPlayer1.SpeedX = 0;
gPlayer2.SpeedY = PLAYER_SPEED;
gPlayer2.SpeedX = 0;
gPlayer1Score = 0;
gPlayer2Score = 0;
gScreen.x = 0;
gScreen.y = 0;
gScreen.w = WINDOW_WIDTH;
gScreen.h = WINDOW_HEIGHT;
// Note to self: Leave this out since color keying NULL will segfault.
// SDL_SetColorKey(gBitmap, SDL_TRUE, SDL_MapRGB(gBitmap->format, 255, 0, 255));
gWindow = SDL_CreateWindow(
WINDOW_TITLE,
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
WINDOW_WIDTH,
WINDOW_HEIGHT,
SDL_WINDOW_MAXIMIZED );
// Render SDL Window
gWindowRenderer = SDL_CreateRenderer( gWindow, -1, 0 );
if( gWindowRenderer == nullptr )
{
success = false;
return success;
}
SDL_RenderSetLogicalSize( gWindowRenderer, WINDOW_WIDTH, WINDOW_HEIGHT );
SDL_SetRenderDrawColor( gWindowRenderer, 0x00, 0x00, 0x00, 0xFF );
return success;
}
// Display Menu Text
bool Menu()
{
bool success = false;
if( LANG == LANG_ENGLISH )
{
// DisplayText("Start (G)ame", 350, 250, 12, 255, 255, 255);
// DisplayText("(Q)uit Game", 350, 270, 12, 255, 255, 255);
success = true;
return success;
}
else if( LANG == LANG_GERMAN )
{
// DisplayText("(G) Spielen", 350, 270, 12, 255, 255, 255);
// DisplayText("(Q) Spiel verlassen", 350, 270, 12, 255, 255, 255);
success = true;
return success;
}
else
{
return success;
}
}
void Render()
{
SDL_RenderClear( gWindowRenderer );
SDL_SetRenderDrawColor( gWindowRenderer, 0x00, 0x00, 0x00, 0xFF );
SDL_RenderFillRect( gWindowRenderer, &gScreen );
SDL_SetRenderDrawColor( gWindowRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderFillRect( gWindowRenderer, &gPlayer1.ScreenLocation );
SDL_RenderFillRect( gWindowRenderer, &gPlayer2.ScreenLocation );
SDL_RenderFillRect( gWindowRenderer, &gBall.ScreenLocation );
SDL_RenderPresent( gWindowRenderer );
}
void Quit()
{
SDL_DestroyWindow( gWindow );
SDL_Quit();
}
I'm not sure what I'm doing wrong and would appreciate a concise answer.
Thank you for your time.
Builds & runs fine on my Debian 10 system, though PLAYER_SPEED is zero so nothing useful happens to gPlayer1.ScreenLocation.y/gPlayer2.ScreenLocation.y in the keyStates if-blocks.
Setting PLAYER_SPEED to something other than zero fixes that on my end.
Related
When I ran the following code, I expected a result where pressing one of three buttons would each trigger a matching state that would render a matching image adjacent to those buttons, allowing me to rotate through fresh images at leisure with each successive button press.
Instead of that happy scenario, any initial button press skips right to the last image...or does nothing...or renders a blank white screen depending on how I fiddle with my placement of the initial state declaration and whether I place the state switch within or without the event handling switch. The whole system breaks and stops dead in its tracks. What is going on, and how do I fix it?
#include <string>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include "cleanup.h"
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Rect* clip = NULL;
const int LONGBUTTON_HEIGHT = 128;
const int LONGBUTTON_WIDTH = 256;
int Screen_Width = 640;
int Screen_Height = 480;
int mouse_x;
int mouse_y;
int alphabutton_x = 0;
int alphabutton_y = 5;
int alphabutton_h = Screen_Width / 10;
int alphabutton_w = Screen_Width / 5;
int betabutton_x = 0;
int betabutton_y = 0.5 * (Screen_Width / 5) + 5;
int betabutton_h = Screen_Width / 10;
int betabutton_w = Screen_Width / 5;
int gammabutton_x = 0;
int gammabutton_y = 1 * (Screen_Width / 5) + 5;
int gammabutton_h = Screen_Width / 10;
int gammabutton_w = Screen_Width / 5;
int alpha_x = Screen_Width / 5;
int alpha_y = 0;
int alpha_h = Screen_Height;
int alpha_w = (4* Screen_Width)/5;
int beta_x = Screen_Width / 5;
int beta_y = 0;
int beta_h = Screen_Height;
int beta_w = (4* Screen_Width)/5;
int gamma_x = Screen_Width / 5;
int gamma_y = 0;
int gamma_h = Screen_Height;
int gamma_w = (4* Screen_Width)/5;
enum alphaButtonSprite {ALPHA_DEFAULT, ALPHA_HOVER, ALPHA_INACTIVE, ALPHA_PRESSED, ALPHA_TOTAL};
enum betaButtonSprite {BETA_DEFAULT, BETA_HOVER, BETA_INACTIVE, BETA_PRESSED, BETA_TOTAL};
enum gammaButtonSprite {GAMMA_DEFAULT, GAMMA_HOVER, GAMMA_INACTIVE, GAMMA_PRESSED, GAMMA_TOTAL};
enum State {ALPHA_STATE, BETA_STATE, GAMMA_STATE};
State state;
SDL_Texture* loadTexture(const std::string& file, SDL_Renderer* renderer)
{
SDL_Texture* texture = IMG_LoadTexture(renderer, file.c_str());
return texture;
}
void renderTexture(SDL_Texture* texture, SDL_Renderer* renderer, SDL_Rect dest,
SDL_Rect* clip = nullptr)
{
SDL_RenderCopy(renderer, texture, clip, &dest);
}
void renderTexture(SDL_Texture* texture, SDL_Renderer* renderer, int x, int y, int h, int w,
SDL_Rect* clip = nullptr)
{
SDL_Rect dest;
dest.x = x;
dest.y = y;
dest.h = h;
dest.w = w;
renderTexture(texture, renderer, dest, clip);
}
// Main Function
int main(int, char**)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("New Window", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, Screen_Width, Screen_Height, SDL_WINDOW_RESIZABLE);
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
SDL_Texture* longbutton_image = loadTexture("longbuttonSpriteSheet.png", renderer);
SDL_Texture* alpha_image = loadTexture("alphaImage.png", renderer);
SDL_Texture* beta_image = loadTexture("betaImage.png", renderer);
SDL_Texture* gamma_image = loadTexture("gammaImage.png", renderer);
state = BETA_STATE;
SDL_Rect alpha_clips[alphaButtonSprite::ALPHA_TOTAL];
for (int i = 0; i < alphaButtonSprite::ALPHA_TOTAL; i++)
{
alpha_clips[i].x = i * LONGBUTTON_WIDTH;
alpha_clips[i].y = 0;
alpha_clips[i].w = LONGBUTTON_WIDTH;
alpha_clips[i].h = LONGBUTTON_HEIGHT;
}
int usealpha_Clip = ALPHA_DEFAULT;
SDL_Rect beta_clips[betaButtonSprite::BETA_TOTAL];
for (int i = 0; i < betaButtonSprite::BETA_TOTAL; i++)
{
beta_clips[i].x = i * LONGBUTTON_WIDTH;
beta_clips[i].y = 4 * LONGBUTTON_HEIGHT;
beta_clips[i].w = LONGBUTTON_WIDTH;
beta_clips[i].h = LONGBUTTON_HEIGHT;
}
int usebeta_Clip = BETA_DEFAULT;
SDL_Rect gamma_clips[gammaButtonSprite::GAMMA_TOTAL];
for (int i = 0; i < gammaButtonSprite::GAMMA_TOTAL; i++)
{
gamma_clips[i].x = i * LONGBUTTON_WIDTH;
gamma_clips[i].y = 5 * LONGBUTTON_HEIGHT;
gamma_clips[i].w = LONGBUTTON_WIDTH;
gamma_clips[i].h = LONGBUTTON_HEIGHT;
}
int usegamma_Clip = GAMMA_DEFAULT;
SDL_Event e;
bool quit = false;
while (!quit)
{
while (SDL_PollEvent(&e))
{
switch (e.type)
{
case SDL_MOUSEBUTTONDOWN:
mouse_x = e.button.x;
mouse_y = e.button.y;
if ((mouse_x <= (alphabutton_x + alphabutton_w)) && (mouse_x > alphabutton_x) &&
(mouse_y <= (alphabutton_y + alphabutton_h)) && (mouse_y > alphabutton_y))
usealpha_Clip = ALPHA_PRESSED;
state = ALPHA_STATE;
if ((mouse_x <= (betabutton_x + betabutton_w)) && (mouse_x > betabutton_x) &&
(mouse_y <= (betabutton_y + betabutton_h)) && (mouse_y > betabutton_y))
usebeta_Clip = BETA_PRESSED;
state = BETA_STATE;
if ((mouse_x <= (gammabutton_x + gammabutton_w)) && (mouse_x > gammabutton_x) &&
(mouse_y <= (gammabutton_y + gammabutton_h)) && (mouse_y > gammabutton_y))
usegamma_Clip = GAMMA_PRESSED;
state = GAMMA_STATE;
break;
}
}
switch (state)
{
case ALPHA_STATE:
SDL_RenderClear(renderer);
renderTexture(longbutton_image, renderer, alphabutton_x, alphabutton_y, alphabutton_h, alphabutton_w, &alpha_clips[usealpha_Clip]);
renderTexture(longbutton_image, renderer, betabutton_x, betabutton_y, betabutton_h, betabutton_w, &beta_clips[usebeta_Clip]);
renderTexture(longbutton_image, renderer, gammabutton_x, gammabutton_y, gammabutton_h, gammabutton_w, &gamma_clips[usegamma_Clip]);
renderTexture(alpha_image, renderer, alpha_x, alpha_y, alpha_h, alpha_w, nullptr);
SDL_RenderPresent(renderer);
break;
case BETA_STATE:
SDL_RenderClear(renderer);
renderTexture(longbutton_image, renderer, alphabutton_x, alphabutton_y, alphabutton_h, alphabutton_w, &alpha_clips[usealpha_Clip]);
renderTexture(longbutton_image, renderer, betabutton_x, betabutton_y, betabutton_h, betabutton_w, &beta_clips[usebeta_Clip]);
renderTexture(longbutton_image, renderer, gammabutton_x, gammabutton_y, gammabutton_h, gammabutton_w, &gamma_clips[usegamma_Clip]);
renderTexture(beta_image, renderer, beta_x, beta_y, beta_h, beta_w, nullptr);
SDL_RenderPresent(renderer);
break;
case GAMMA_STATE:
SDL_RenderClear(renderer);
renderTexture(longbutton_image, renderer, alphabutton_x, alphabutton_y, alphabutton_h, alphabutton_w, &alpha_clips[usealpha_Clip]);
renderTexture(longbutton_image, renderer, betabutton_x, betabutton_y, betabutton_h, betabutton_w, &beta_clips[usebeta_Clip]);
renderTexture(longbutton_image, renderer, gammabutton_x, gammabutton_y, gammabutton_h, gammabutton_w, &gamma_clips[usegamma_Clip]);
renderTexture(gamma_image, renderer, gamma_x, gamma_y, gamma_h, gamma_w, nullptr);
SDL_RenderPresent(renderer);
break;
}
}
//Destroy the various items
cleanup(longbutton_image, alpha_image, beta_image, gamma_image, renderer, window);
IMG_Quit();
SDL_Quit();
return 0;
}
Found the answer to this one. In the mousebuttondown event switch cases, just put the state code before the useclip code. Why it makes a difference, I don't know, but it works. Cough...Also, doing it in the mousebuttonup switch cases instead of mousebuttondown works even better.
I am new into SDL2(version 2.0.10) and I teach it from Lazy Foo tutorial. In Lesson of The ViewPort example code render me only first image in left viewport and others not. Render of rects in different viewports don't work either. What I am doing wrong when I want render rects in different viewports like this:
while( !quit ){
while( SDL_PollEvent( &e ) != 0 ){
if( e.type == SDL_QUIT ){
quit = true;
}
}
//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );
//Top left corner viewport
SDL_Rect topLeftViewport;
topLeftViewport.x = 0;
topLeftViewport.y = 0;
topLeftViewport.w = SCREEN_WIDTH / 2;
topLeftViewport.h = SCREEN_HEIGHT / 2;
SDL_RenderSetViewport( gRenderer, &topLeftViewport );
SDL_Rect fillRect = { 10, 10, 100, 100 };
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0x00, 0x00, 0xFF );
SDL_RenderFillRect( gRenderer, &fillRect );
SDL_Rect topRightViewport;
topRightViewport.x = SCREEN_WIDTH / 2;
topRightViewport.y = 0;
topRightViewport.w = SCREEN_WIDTH / 2;
topRightViewport.h = SCREEN_HEIGHT / 2;
SDL_RenderSetViewport( gRenderer, &topRightViewport );
SDL_Rect fillRect2 = { 10, 10, 100, 100 };
SDL_SetRenderDrawColor( gRenderer, 0x00, 0xFF, 0x00, 0xFF );
SDL_RenderFillRect( gRenderer, &fillRect2 );
SDL_RenderPresent( gRenderer );
}
As I saw, you are drawing a rect at 10, 10 of 100x100 two times, this coordinates doesn't take account of the viewport coordinates. And in a way, if you want to do this by changing the viewport clipping is not relevant... just draw the square where you want.
UPDATE: I tried multiples viewports. Here is a working example. You can choose between the renderer (with viewports) or the classic way.
main.cpp :
#include "Application.hpp"
int
main (int argc, char * argv[])
{
Application app("SDL 2 Test", 800, 600, true);
return app.mainLoop();
}
Application.hpp :
#ifndef APPLICATION_HPP
#define APPLICATION_HPP
#include <SDL2/SDL.h>
class Application
{
public:
Application (const char * title = "UnknownApplication", int baseWidth = 640, int baseHeight = 480, bool useRenderer = false) noexcept;
~Application ();
int mainLoop () noexcept;
private:
int m_width = 0;
int m_height = 0;
SDL_Window * m_window = nullptr;
SDL_Renderer * m_renderer = nullptr;
bool m_useRenderer = false;
bool m_isRunning = false;
};
#endif /* APPLICATION_HPP */
Application.cpp :
#include "Application.hpp"
#include <iostream>
#include <SDL2/SDL_image.h>
Application::Application (const char * title, int baseWidth, int baseHeight, bool useRenderer) noexcept
: m_width(baseWidth), m_height(baseHeight), m_useRenderer(useRenderer)
{
if ( SDL_Init(SDL_INIT_VIDEO) != 0 )
{
std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
return;
}
m_window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, m_width, m_height, SDL_WINDOW_SHOWN);
if ( m_window == nullptr )
{
std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
return;
}
if ( m_useRenderer )
{
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
if ( m_renderer == nullptr )
{
std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() << std::endl;
return;
}
}
m_isRunning = true;
}
int
Application::mainLoop () noexcept
{
SDL_Event event;
const auto filepath = "SET_IMAGE_YOU_WANT_HERE";
auto surface = IMG_Load(filepath);
if ( surface == nullptr )
{
std::cerr << "Unable to read image file : " << filepath << std::endl;
return 1;
}
SDL_Rect logoPosition = {8, 8, 32, 32};
if ( m_useRenderer )
{
auto texture = SDL_CreateTextureFromSurface(m_renderer, surface);
SDL_Rect screenA = {0, 0, m_width / 2, m_height / 2};
SDL_Rect screenB = {m_width / 2, 0, m_width / 2, m_height / 2};
while ( m_isRunning )
{
while ( SDL_PollEvent(&event) != 0 )
{
if ( event.type == SDL_QUIT )
m_isRunning = false;
}
SDL_SetRenderDrawColor(m_renderer, 0, 0, 0, 255);
SDL_RenderClear(m_renderer);
SDL_RenderSetViewport(m_renderer, &screenA);
SDL_SetRenderDrawColor(m_renderer, 255, 0, 0, 255);
SDL_RenderFillRect(m_renderer, nullptr);
SDL_RenderCopy(m_renderer, texture, nullptr, &logoPosition);
SDL_RenderSetViewport(m_renderer, &screenB);
SDL_SetRenderDrawColor(m_renderer, 0, 255, 0, 255);
SDL_RenderFillRect(m_renderer, nullptr);
SDL_RenderCopy(m_renderer, texture, nullptr, &logoPosition);
SDL_RenderPresent(m_renderer);
}
SDL_DestroyTexture(texture);
}
else
{
auto windowSurface = SDL_GetWindowSurface(m_window);
while ( m_isRunning )
{
while ( SDL_PollEvent(&event) != 0 )
{
if ( event.type == SDL_QUIT )
m_isRunning = false;
}
SDL_FillRect(windowSurface, nullptr, SDL_MapRGB(windowSurface->format, 0xFF, 0x00, 0xFF));
//SDL_BlitSurface(surface, nullptr, windowSurface, &logoPosition);
SDL_BlitScaled(surface, nullptr, windowSurface, &logoPosition);
SDL_UpdateWindowSurface(m_window);
}
}
SDL_FreeSurface(surface);
return 0;
}
Application::~Application (void)
{
if ( m_renderer != nullptr )
SDL_DestroyRenderer(m_renderer);
if ( m_window != nullptr )
SDL_DestroyWindow(m_window);
SDL_Quit();
}
I want to create my first game.
Nothing special, just blue rectangle moving when WSAD keys are pressed.
The problem is that when I run my game, there are bugs with rectangle(See image below). Bugs appears only during horizontal movement, and not vertical.
Which is interesting, when I changed line:
renderer = SDL_CreateRenderer(display, -1, SDL_RENDERER_ACCELERATED)
to:
renderer = SDL_CreateRenderer(display, -1, SDL_RENDERER_SOFTWARE)
everything is OK
I am using Windows 10, MinGw with CMake(C++14), and SDL 2.0.8, Intel core i5 7th gen, Radeon M7 R465
Im my code OnRender function is responsible for rendering, maybe I made something wrong in it?(Function in my code posted at end of question)
I am also using SDL_WINDOW_OPENGL flag to create my window, but changing it to SDL_WINDOW_SHOWN doesn't change anything.
#include <SDL2/SDL.h>
class Game
{
private:
SDL_Surface *display_surf = nullptr;
SDL_Renderer *renderer = nullptr;
SDL_Window *display = nullptr;
private:
bool running, prW = false, prS = false, prD = false, prA = false;
int x, y;
int spd_y, spd_x;
int scr_w, scr_h;
public:
Game();
int OnExecute();
public:
bool OnInit();
void OnEvent( SDL_Event *event );
void OnLoop();
void OnRender();
void OnCleanup();
};
Game::Game()
{
running = false;
}
int Game::OnExecute()
{
if( !OnInit() )
{
return -1;
}
running = true;
SDL_Event event;
while( running )
{
while( SDL_PollEvent( &event ) )
{
OnEvent( &event );
}
OnLoop();
OnRender();
SDL_Delay( 1 );
}
OnCleanup();
return 0;
}
bool Game::OnInit()
{
if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
{
return false;
}
SDL_DisplayMode dspm;
if( SDL_GetDesktopDisplayMode( 0, &dspm ) < 0 )
{
return false;
}
scr_h = dspm.h;
scr_w = dspm.w;
if( ( display = SDL_CreateWindow( "Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1920, 1080,
SDL_WINDOW_OPENGL ) ) == nullptr )
{
return false;
}
display_surf = SDL_GetWindowSurface( display );
if( ( renderer = SDL_CreateRenderer( display, -1, SDL_RENDERER_ACCELERATED ) ) == nullptr )
{
return false;
}
x = 0;
y = 0;
spd_x = 0;
spd_y = 0;
SDL_SetWindowFullscreen( display, SDL_WINDOW_FULLSCREEN );
return true;
}
void Game::OnEvent( SDL_Event *event )
{
if( event->type == SDL_QUIT )
{
running = false;
return;
}
switch( event->type )
{
case SDL_KEYDOWN:
switch( event->key.keysym.sym )
{
case SDLK_w:
if( prS )
{
spd_y = 0;
}
else
{
spd_y = -5;
}
prW = true;
break;
case SDLK_s:
if( prW )
{
spd_y = 0;
}
else
{
spd_y = 5;
}
prS = true;
break;
case SDLK_d:
if( prA )
{
spd_x = 0;
}
else
{
spd_x = 5;
}
prD = true;
break;
case SDLK_a:
if( prD )
{
spd_x = 0;
}
else
{
spd_x = -5;
}
prA = true;
break;
default:
return;
}
break;
case SDL_KEYUP:
switch( event->key.keysym.sym )
{
case SDLK_w:
if( !prS )
{
spd_y = 0;
}
else
{
spd_y = 5;
}
prW = false;
break;
case SDLK_s:
if( !prW )
{
spd_y = 0;
}
else
{
spd_y = -5;
}
prS = false;
break;
case SDLK_a:
if( !prD )
{
spd_x = 0;
}
else
{
spd_x = 5;
}
prA = false;
break;
case SDLK_d:
if( !prA )
{
spd_x = 0;
}
else
{
spd_x = -5;
}
prD = false;
break;
default:
return;
}
default:
return;
}
}
void Game::OnLoop()
{
x += spd_x;
y += spd_y;
if( x < 0 )
{
x = 0;
}
else if( x > scr_w - 100 )
{
x = scr_w - 100;
}
if( y < 0 )
{
y = 0;
}
else if( y > scr_h - 100 )
{
y = scr_h - 100;
}
}
void Game::OnRender()
{
SDL_SetRenderDrawColor( renderer, 0, 0, 0, 0x00 );
SDL_RenderClear( renderer );
SDL_Rect charc;
charc.x = x;
charc.y = y;
charc.w = 100;
charc.h = 100;
SDL_SetRenderDrawColor( renderer, 0, 0, 0xff, 0 );
SDL_RenderFillRect( renderer, &charc );
SDL_RenderPresent( renderer );
}
void Game::OnCleanup()
{
SDL_DestroyWindow( display );
SDL_Quit();
}
int main( int argc, char** argv )
{
Game game;
return game.OnExecute();
}
Looks a lot like tearing caused by a high frame-rate & lack of vsync.
I can get tear-less drawing by passing SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC to flags on SDL_CreateRenderer():
#include <SDL2/SDL.h>
#include <iostream>
class Game
{
private:
SDL_Renderer *renderer = nullptr;
SDL_Window *display = nullptr;
private:
bool running, prW = false, prS = false, prD = false, prA = false;
int x, y;
int spd_y, spd_x;
int scr_w, scr_h;
public:
Game();
int OnExecute();
public:
bool OnInit();
void OnEvent( SDL_Event *event );
void OnLoop();
void OnRender();
void OnCleanup();
};
Game::Game()
{
running = false;
}
int Game::OnExecute()
{
if( !OnInit() )
{
return -1;
}
running = true;
SDL_Event event;
Uint32 beg = SDL_GetTicks();
size_t frames = 0;
while( running )
{
while( SDL_PollEvent( &event ) )
{
OnEvent( &event );
}
OnLoop();
OnRender();
frames++;
Uint32 end = SDL_GetTicks();
if( end - beg > 1000 )
{
std::cout << "Frame time: " << ( end - beg ) / frames << " ms" << std::endl;
beg = end;
frames = 0;
}
}
OnCleanup();
return 0;
}
bool Game::OnInit()
{
if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
{
return false;
}
if( ( display = SDL_CreateWindow( "Game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, 0 ) ) == nullptr )
{
return false;
}
scr_w = 1280;
scr_h = 720;
Uint32 rflags = SDL_RENDERER_ACCELERATED;
rflags |= SDL_RENDERER_PRESENTVSYNC;
if( ( renderer = SDL_CreateRenderer( display, -1, rflags ) ) == nullptr )
{
return false;
}
x = 0;
y = 0;
spd_x = 0;
spd_y = 0;
return true;
}
void Game::OnEvent( SDL_Event *event )
{
if( event->type == SDL_QUIT )
{
running = false;
return;
}
switch( event->type )
{
case SDL_KEYDOWN:
switch( event->key.keysym.sym )
{
case SDLK_w:
if( prS )
{
spd_y = 0;
}
else
{
spd_y = -5;
}
prW = true;
break;
case SDLK_s:
if( prW )
{
spd_y = 0;
}
else
{
spd_y = 5;
}
prS = true;
break;
case SDLK_d:
if( prA )
{
spd_x = 0;
}
else
{
spd_x = 5;
}
prD = true;
break;
case SDLK_a:
if( prD )
{
spd_x = 0;
}
else
{
spd_x = -5;
}
prA = true;
break;
default:
return;
}
break;
case SDL_KEYUP:
switch( event->key.keysym.sym )
{
case SDLK_w:
if( !prS )
{
spd_y = 0;
}
else
{
spd_y = 5;
}
prW = false;
break;
case SDLK_s:
if( !prW )
{
spd_y = 0;
}
else
{
spd_y = -5;
}
prS = false;
break;
case SDLK_a:
if( !prD )
{
spd_x = 0;
}
else
{
spd_x = 5;
}
prA = false;
break;
case SDLK_d:
if( !prA )
{
spd_x = 0;
}
else
{
spd_x = -5;
}
prD = false;
break;
default:
return;
}
default:
return;
}
}
void Game::OnLoop()
{
x += spd_x;
y += spd_y;
if( x < 0 )
{
x = 0;
}
else if( x > scr_w - 100 )
{
x = scr_w - 100;
}
if( y < 0 )
{
y = 0;
}
else if( y > scr_h - 100 )
{
y = scr_h - 100;
}
}
void Game::OnRender()
{
SDL_SetRenderDrawColor( renderer, 0, 0, 0, 0x00 );
SDL_RenderClear( renderer );
SDL_Rect charc;
charc.x = x;
charc.y = y;
charc.w = 100;
charc.h = 100;
SDL_SetRenderDrawColor( renderer, 0, 0, 0xff, 0 );
SDL_RenderFillRect( renderer, &charc );
SDL_Delay( 1 );
SDL_RenderPresent( renderer );
}
void Game::OnCleanup()
{
SDL_DestroyWindow( display );
SDL_Quit();
}
int main( int argc, char** argv )
{
Game game;
return game.OnExecute();
}
If I just pass SDL_RENDERER_ACCELERATED I get tearing and a vastly higher frame-rate.
Make sure your OS isn't configured to disable vsync by default.
Many developers seem to get some unwanted behaviours when enabling SDL_RENDERER_ACCELERATED flag on SDL 2.0.8.
A ticket has been opened in libsdl's bugzilla (https://bugzilla.libsdl.org/show_bug.cgi?id=4110). It's about another problem but issue with hardware rendering is mentioned in description.
For now, I use software rendering (SDL_RENDERER_SOFTWARE) as a fallback. Not really what I wanted to do but now I get the expected result.
I'll try PREVENTSYNC...
I started writing a little game but something with the image is not working. They're working fine at the beginning but after some moments there become black and then they disappear.
Don't be worried about the long code the relevant things with the images happen mostly in the following methods: build_mode_draw() and play_mode_draw().
I tested the program with a blue cube instead of a image and it worked fine
Probably I don't really understand how the image gets loaded
#include <SDL2/SDL.h>
#define WindowWidth 1500
#define WindowHight 800
#define ArrayGrosseBuildMode 100
#define StartFensterBlockmenge 10 // in Plockgröße bezüglich der kleineren Achse
bool end = false;
bool programm_part_run = true;
unsigned int play_mode_speed = 0;
unsigned int counter;
unsigned int counter_2;
unsigned int counter_3;
unsigned int blocksize;
unsigned int blocks_fit_in_X;
unsigned int blocks_fit_in_Y;
unsigned int play_mode_blockamount;
//unsigned int blockamount = 0;
bool build_mode_block_pos[ArrayGrosseBuildMode][ArrayGrosseBuildMode];
unsigned int play_mode_block_pos_X[WindowWidth]; // Fächer beschreiben
unsigned int play_mode_block_pos_Y[WindowHight]; // ||
//mouse variables
unsigned short int pressed_mouse_button = 0; // 0 = no , 1 = left , mouse Button pressed
unsigned int MouseX;
unsigned int MouseY;
//keyboard variables
//set window
SDL_Window* window = NULL;
//set renderer
SDL_Renderer* renderer = NULL;
//set event
SDL_Event event;
void input()
{
SDL_PollEvent(&event);
// reset variables
pressed_mouse_button = 0; // set to no mouse button pressed
switch(event.type)
{
case SDL_QUIT:
end = true;
programm_part_run = false;
break;
case SDL_MOUSEMOTION:
MouseX = event.motion.x;
MouseY = event.motion.y;
break;
case SDL_MOUSEBUTTONDOWN:
switch(event.button.button)
{
case SDL_BUTTON_LEFT:
pressed_mouse_button = 1;
break;
}
break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym)
{
case SDLK_SPACE:
programm_part_run = false;
break;
}
}
}
void put_build_mode_grid_in_renderer()
{
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
for(counter = 0; counter <= blocks_fit_in_Y; counter = counter + 1)
{
SDL_RenderDrawLine(renderer,0,counter * blocksize,blocks_fit_in_X*blocksize,counter * blocksize);
}
for(counter = 0; counter <= blocks_fit_in_X; counter = counter + 1)
{
SDL_RenderDrawLine(renderer,counter * blocksize,0,counter * blocksize,blocks_fit_in_Y*blocksize);
}
}
void build_mode_draw()
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0);
SDL_RenderClear(renderer);
put_build_mode_grid_in_renderer();
SDL_Surface * image = SDL_LoadBMP("stealcube.bmp");
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer,image);
SDL_FreeSurface(image);
for(counter = 0; counter <= blocks_fit_in_X; counter = counter + 1)
{
for(counter_2 = 0; counter_2 <= blocks_fit_in_Y; counter_2 = counter_2 + 1)
{
if(build_mode_block_pos[counter][counter_2] == true)
{
SDL_Rect dstrect = { counter * blocksize, counter_2 * blocksize, blocksize, blocksize};
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
}
}
}
SDL_RenderPresent(renderer);
}
void build_mode()
{
while(programm_part_run)
{
input();
if(pressed_mouse_button == 1)
{
build_mode_block_pos[MouseX/blocksize][MouseY/blocksize] = true;
}
build_mode_draw();
}
}
void play_mode_draw()
{
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 0);
SDL_RenderClear(renderer);
SDL_Surface * image = SDL_LoadBMP("stealcube.bmp");
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer,image);
SDL_FreeSurface(image);
for(counter = 0; counter < play_mode_blockamount; counter = counter + 1)
{
SDL_Rect dstrect = { play_mode_block_pos_X[counter], play_mode_block_pos_Y[counter], blocksize, blocksize};
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
}
SDL_RenderPresent(renderer);
}
void play_mode()
{
counter_3 = 0;
for(counter = 0; counter <= blocks_fit_in_X; counter = counter + 1)
{
for(counter_2 = 0; counter_2 <= blocks_fit_in_Y; counter_2 = counter_2 + 1)
{
if(build_mode_block_pos[counter][counter_2] == true)
{
play_mode_block_pos_X[counter_3] = counter*blocksize;
play_mode_block_pos_Y[counter_3] = counter_2*blocksize;
counter_3 = counter_3 + 1;
}
}
}
play_mode_blockamount = counter_3;
while(programm_part_run)
{
for(counter = 0; counter < play_mode_speed; counter = counter + 1)
{
input();
SDL_Delay(1);
}
for(counter = 0; counter <= play_mode_blockamount; counter = counter + 1)
{
if(play_mode_block_pos_Y[counter] < blocks_fit_in_Y * blocksize - blocksize)
{
play_mode_block_pos_Y[counter] = play_mode_block_pos_Y[counter] + 1;
}
}
play_mode_draw();
}
}
int main (int argc, char** argv)
{
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow
(
"Test Fenster :)", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
WindowWidth,
WindowHight,
SDL_WINDOW_SHOWN
);
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED);
//setup
if(WindowWidth < WindowHight)
{
blocksize = WindowWidth/StartFensterBlockmenge;
}
else
{
blocksize = WindowHight/StartFensterBlockmenge;
}
blocks_fit_in_X = WindowWidth/blocksize;
blocks_fit_in_Y = WindowHight/blocksize;
while(!end)
{
programm_part_run = true;
build_mode();
programm_part_run = true;
play_mode();
}
}
#include "SDL/SDL.h"
#include "SDL/SDL_Image.h"
#include <string>
using namespace std;
//Const screen variables
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;
const char* SCREEN_CAPTION = "Pyro";
const float GRAVITY = 9.8; //Metres per second **NEEDS TO BE ADJUSTED BECAUSE IT'S IN METRES, NOT PIXELS**
const int jumpHeight = 10;
//Non-Const variables
bool running = true;
bool isJumping = true;
int jump = 0;
int frame = 0;
int level = 1;
SDL_Event event;
Uint8 *keystate = NULL;
//SDL Surfaces
SDL_Surface *screen = NULL;
SDL_Surface *background = NULL;
SDL_Surface *sprite = NULL;
SDL_Surface *bEnemy[10];
//Structs
typedef struct entity {
int health;
int damage;
SDL_Rect hitbox;
bool evolved;
} playerType, enemyType;
playerType player;
enemyType basicEnemy[10];
//Functions
SDL_Surface *loadImage( std::string filename )
{
SDL_Surface *loadedImage = NULL;
SDL_Surface *optimizedImage = NULL;
loadedImage = IMG_Load( filename.c_str() );
if( loadedImage != NULL )
{
optimizedImage = SDL_DisplayFormatAlpha( loadedImage );
SDL_FreeSurface( loadedImage );
}
return optimizedImage;
}
void applySurface( int x, int y, SDL_Surface* source, SDL_Surface* location )
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface( source, NULL, location, &offset );
}
//Main Function
int main( int argc, char* argv[] )
{
if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
{
return 1;
}
screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );
if( screen == NULL )
{
return 1;
}
SDL_WM_SetCaption( SCREEN_CAPTION, NULL );
background = loadImage( "images/background.png" );
sprite = loadImage( "images/player.png" );
SDL_FreeSurface( sprite );
SDL_FreeSurface( background );
while( running )
{
//Main game loop
if( SDL_PollEvent( &event ) )
{
switch( event.type )
{
case SDL_QUIT:
running = false;
break;
case SDL_KEYDOWN:
switch( event.key.keysym.sym )
{
case SDLK_q:
running = false;
break;
}
break;
}
}
keystate = SDL_GetKeyState(NULL);
if( keystate[SDLK_q] ) player.evolved = !player.evolved;
if( keystate[SDLK_UP] )
{
if(isJumping != true)
{
isJumping = true;
}
}
if( keystate[SDLK_LEFT] ) player.hitbox.x -= 1;
if( keystate[SDLK_RIGHT] ) player.hitbox.y += 1;
//Window collision
if( player.hitbox.x < 0 ) {player.hitbox.x = 0;}
else if( player.hitbox.x > SCREEN_WIDTH - player.hitbox.w ) {player.hitbox.x = SCREEN_WIDTH - player.hitbox.w;}
if( player.hitbox.y < 0 ) {player.hitbox.y = 0;}
else if( player.hitbox.y > SCREEN_HEIGHT - player.hitbox.h ) {player.hitbox.y = SCREEN_HEIGHT - player.hitbox.h;}
//Jumping
if( isJumping == true )
{
if(jump >= jumpHeight)
{
jump--;
player.hitbox.y++;
}else {
jump++;
player.hitbox.y--;
}
}
//Updating the screen
applySurface( 0, 0, background, screen );
applySurface( player.hitbox.x, player.hitbox.y, sprite, screen );
if( SDL_Flip( screen ) == -1 )
{
return 1;
}
}
SDL_Quit();
return 0;
}
^ That is the exact code I have. When I run the file it immediately closes. It is compiled with: g++ -o myprogram.exe mysource.cpp -lmingw32 -lSDLmain -lSDL -lSDL_image -static-libgcc -static-libstdc++.
The files I have linked to do exist; they are currently placeholders (background is some random png image I found, and the player is an image of 8-bit mario).
How do I stop my program from closing immediately?
SDL_FreeSurface( sprite );
SDL_FreeSurface( background );
This is where your problem lies.
These lines should appear at the end of the program right before you call SDL_Quit().
Currently, you're blitting freed surfaces onto the window.