exc_bad_access error xcode5 c++ sdl2 - c++

I've been toying with this code a guy on youtube attached to one of his tutorial videos... I've run into a few problems that I cant seem to get past... The current one is this 'EXC_BAD_ACCESS' error... From what I've researched briefly online these errors occur when you improperly use the stack and allocate memory to things that no longer exist or something like that... I just can't pinpoint what I'm doing wrong exactly... I've seen some troubleshooting done -- but nothing for xcode 5 yet
The error shows up in a class called 'cSprite.cpp':
// ~~~~~~~~~~~~~~~~~~~ cSprite.cpp ~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "stdafx.h"
#include "cSprite.h"
//#include "gMain.h"
//#include <SDL2/SDL_main.h>
//#include <SDL2/SDL.h>
//#include "cSDL_Setup.h"
using namespace std;
void draw(SDL_Renderer, SDL_Texture, SDL_Rect);
cSprite::cSprite(SDL_Renderer* passed_renderer, string filePath, int x, int y, int w, int h)
{
renderer = passed_renderer;
//... image
image = NULL;
image = IMG_LoadTexture(renderer,filePath.c_str());
// image error message
if (image == NULL)
{
cout<<"Couldnt load image..."<<filePath.c_str()<<endl;
}
//image dimensions
rect->x = x; // ***** Error: Thread 1:EXC_BAD_ACCESS(code=EXC_1386_GPFLT)
rect->y = y;
rect->w = w;
rect->h = h;
};
cSprite::~cSprite(void)
{
SDL_DestroyTexture(image);
}
//get methods
//SDL_Rect* cSprite::getRect()
//{
// return rect;
//}
//get methods
//SDL_Texture* cSprite::getImage()
//{
// return image;
//}
void cSprite::draw()
{
SDL_RenderCopy(renderer, image, NULL, rect);
}
... the line 'rect->x = x;'
gets the error I put beside it...but idk what else could be causing it... I was hoping someone could explain why EXC_BAD_ACCESS errors even occur... and/or where in the rest of my code it could be coming from...
here's my 3 other .cpp files
....
// ~~~~~~~~~~~~~~~~~~~~~~~~ gMain.cpp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <iostream>
#include "stdafx.h"
#include "gMain.h"
using namespace std;
gMain::gMain(int passed_screenWidth, int passed_screenHeight)
{
screenWidth = passed_screenWidth;
screenHeight = passed_screenHeight;
// quit boolean
quit = false;
// create instance of cSDL class
csdl_setup = new cSDL_Setup(&quit, screenWidth, screenHeight);
grass = new cSprite(csdl_setup->getRenderer(), "/Users/jamesbryant/Desktop/nuGame/nuGame/images.jpeg", screenWidth, screenHeight, screenWidth, screenHeight);
bruce_Lee = new cSprite(csdl_setup->getRenderer(), "/Users/jamesbryant/Desktop/nuGame/nuGame/lee.bmp", 300, 300, 200, 200);
}
gMain::~gMain(void)
{
}
void gMain::gameLoop(void)
{
// game loop logic
while (!&quit && csdl_setup->getMainEvent() ->type != SDL_QUIT)
{
csdl_setup->begin();
grass->draw();
bruce_Lee->draw();
csdl_setup->end();
}
}
....
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ cSDL_Setup.cpp ~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "stdafx.h"
#include "cSDL_Setup.h"
//#include "gMain.h"
using namespace std;
cSDL_Setup::cSDL_Setup(bool* quit, int screenWidth, int screenHeight)
{
// create window
window = NULL;
window = SDL_CreateWindow("rWDY_pWDR", 400, 400, screenWidth, screenHeight, SDL_WINDOW_RESIZABLE);
// if window couldnt be created...
if (window == NULL)
{
cout<<"Window couldnt be created..."<<endl;
*quit = true;
//exit(0);
}
//create renderer
renderer = NULL;
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// initialize images
mainEvent = new SDL_Event();
}
cSDL_Setup::~cSDL_Setup(void)
{
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
delete mainEvent;
}
//get methods
SDL_Renderer* cSDL_Setup::getRenderer()
{
return renderer;
}
SDL_Event* cSDL_Setup::getMainEvent()
{
return mainEvent;
}
void cSDL_Setup::begin()
{
SDL_PollEvent(mainEvent);
SDL_RenderClear(renderer);
}
void cSDL_Setup::end()
{
SDL_RenderPresent(renderer);
}
....
// ~~~~~~~~~~~~~~~~~~~~~~~~~~ firstGame.cpp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include "stdafx.h"
#include "cSDL_Setup.h"
//#include "gMain.h"
using namespace std;
cSDL_Setup::cSDL_Setup(bool* quit, int screenWidth, int screenHeight)
{
// create window
window = NULL;
window = SDL_CreateWindow("rWDY_pWDR", 400, 400, screenWidth, screenHeight, SDL_WINDOW_RESIZABLE);
// if window couldnt be created...
if (window == NULL)
{
cout<<"Window couldnt be created..."<<endl;
*quit = true;
//exit(0);
}
//create renderer
renderer = NULL;
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// initialize images
mainEvent = new SDL_Event();
}
cSDL_Setup::~cSDL_Setup(void)
{
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
delete mainEvent;
}
//get methods
SDL_Renderer* cSDL_Setup::getRenderer()
{
return renderer;
}
SDL_Event* cSDL_Setup::getMainEvent()
{
return mainEvent;
}
void cSDL_Setup::begin()
{
SDL_PollEvent(mainEvent);
SDL_RenderClear(renderer);
}
void cSDL_Setup::end()
{
SDL_RenderPresent(renderer);
}
... here's what my cSprite.h file looks like right now:
#ifndef __game__cSprite__
#define __game__cSprite__
#pragma once
#include <iostream>
#include <SDL2/SDL_main.h>
#include <SDL2/SDL.h>
using namespace std;
class cSprite
{
public:
cSprite(SDL_Renderer* passed_renderer, string filePath, int x, int y, int w, int h);
~cSprite(void);
void draw();
private:
SDL_Texture* image = NULL;
SDL_Rect* rect = NULL;
SDL_Renderer* renderer = NULL;
};
#endif /* defined(__game__cSprite__) */

You didn't post your header for cSprite. However, I imagine that rect is an SDL_Rect*.
The function getting the error is cSprite::cSprite(), ie. the constructor. One of its duties is to initialize all of the class members to reasonable values.
However, I do not see any code that initializes rect to point to anything. The error you're currently getting suggests you're dereferencing a bum pointer, and that's consistent with this observation.
You either need to allocate an SDL_Rect to assign to rect, or you should change rect to just be SDL_Rect, not SDL_Rect*. If you make the latter change, then you will also need to change all of your rect-> to rect., and pass a pointer to rect in the SDL calls that require it (ie. SDL_RenderCopy(renderer, image, NULL, &rect);)

Related

"x is not a class or namespace name" when trying to use functions between namespaces

I am new to C++ and am currently trying to implement namespaces.
Here, you can see, I have a namespace, GameEngine_CORE, where all the behind-the-scenes work is done. I have the namespace SandBox, which is where the user actually would write their game.
I need SandBox to be able to access some of the variables in GameEngine_CORE, which seems to work fine (I have put a list of using GameEngine_CORE::... to save the user's time).
However, I also need GameEngine_CORE to be able to call the start() and update() function in SandBox, but upon compilation I get an error every time I try and call something from SandBox, eg: SandBox::start();
'SandBox' is not a class or namespace name
I am coding in Visual Studio, and compiling in Debug mode for Windows x64.
#pragma once
#include "SDL.h"
#undef main
#include <iostream>
#include <math.h>
#include <vector>
#include <stdio.h> // Temp
#include "Vec2.h"
#include "Log.h"
#include "Time.h"
#include "Boundary.h"
#include "Ray.h"
#include "Particle.h"
#include "Polygon.h"
#include "Path.h"
#include "Circle.h"
namespace GameEngine_CORE {
// Renderer
SDL_Renderer* renderer;
SDL_Window* window;
bool isRunning;
bool fullscreen;
// Mouse
int mouseX;
int mouseY;
Uint32 mouse;
// Keyboard
Vec2 arrowInp;
// Collision Stack
std::vector<Boundary> collisionStack;
// Log
Log logger;
// Physics
Time timer;
Time clockTime;
float deltaTime;
Vec2 gravity = Vec2(0, -9.8f);
void handleEvents();
void render();
void createWindow();
void destroyWindow();
// For the SandBox Program
void SandBox::start();
void SandBox::update();
int main() { // Entry Point
double m_LastClock = 0.0;
clockTime.StartTimer(); // Start Counter
SandBox::start(); // For user
while (isRunning) {
double m_CurrentClock;
m_CurrentClock = clockTime.GetTimer(); // Get Counter
deltaTime = (float)(m_CurrentClock - m_LastClock);
if (deltaTime > 0.15f) {
deltaTime = 0.15f;
}
handleEvents();
render();
m_LastClock = m_CurrentClock;
}
destroyWindow();
return 0;
}
// Initialise the window to draw to
void createWindow() {
// Set window size and type
fullscreen = true;
Uint32 flags = 0;
flags = SDL_WINDOW_RESIZABLE;
if (fullscreen) {
flags = flags | SDL_WINDOW_MAXIMIZED;
}
if (SDL_Init(SDL_INIT_EVERYTHING) == 0) {
logger.Message("Subsystems Initialised");
window = SDL_CreateWindow("2D Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 300, 300, flags);
if (window) {
logger.Message("Window Created");
// Minimum window size
SDL_SetWindowMinimumSize(window, 1000, 1000);
}
// Create Renderer for window
renderer = SDL_CreateRenderer(window, -1, 0);
if (renderer) {
SDL_SetRenderDrawColor(renderer, 121, 121, 121, 255);
logger.Message("Renderer Created");
// Set how to blend alphas and colours
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
isRunning = true;
}
}
}
void destroyWindow() {
// Frees memory associated with renderer and window
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window); //error here
SDL_Quit();
}
// Handles SDL events
void handleEvents() {
// Check for quit
SDL_Event event;
SDL_PollEvent(&event);
// Check we have latest inputs
SDL_PumpEvents();
// Reset inputs
arrowInp.x = 0; arrowInp.y = 0;
// If we get quit event, stop running and free up memory
switch (event.type) {
case SDL_QUIT:
isRunning = false;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_LEFT: arrowInp.x = -1; break;
case SDLK_RIGHT: arrowInp.x = 1; break;
}
switch (event.key.keysym.sym)
{
case SDLK_UP: arrowInp.y = -1; break;
case SDLK_DOWN: arrowInp.y = 1; break;
}
break;
default:
break;
}
}
// Render Function
void render() {
// Set back ground colour and clear renderer every frame
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SandBox::update();
SDL_RenderPresent(renderer); // Draw to the screen
}
}
using GameEngine_CORE::createWindow;
using GameEngine_CORE::deltaTime;
using GameEngine_CORE::mouse;
using GameEngine_CORE::mouseX;
using GameEngine_CORE::mouseY;
using GameEngine_CORE::renderer;
using GameEngine_CORE::arrowInp;
using GameEngine_CORE::collisionStack;
namespace SandBox { // Where the user of the engine would write code
// Drawing Vars
Particle particle = Particle(50, 359.99f, Vec2(900, 700));
Boundary boundaries[] = { Boundary(Vec2(800, 0), Vec2(800, 1000)), Boundary(Vec2(300, 300), Vec2(400, 700)), Boundary(Vec2(300, 700), Vec2(800, 800)) };
GameEngine::Polygon square = GameEngine::Polygon(Vec2(800, 400), 45, Vec2(400, 200), 4);
GameEngine::Polygon poly1 = GameEngine::Polygon(Vec2(200, 400), Vec2(200, 200), 6);
GameEngine::Polygon ellipse = GameEngine::Polygon(Vec2(1200, 600), Vec2(200, 400), 64);
Path path = Path(Vec2(500, 100));
Circle circle = Circle(100, Vec2(800, 500));
GameEngine::Polygon player = GameEngine::Polygon(Vec2(700, 400), 0, Vec2(60, 60), 16);
float moveSpeed;
Vec2 velocity;
float rotator = 0;
// Functions
static void collision();
static void start() { // Called when the program starts
createWindow();
moveSpeed = 1000.0f;
path.addPoints(std::vector<Vec2> { Vec2(200, 100), Vec2(200, 800), Vec2(350, 800), Vec2(700, 650), Vec2(400, 400), Vec2(200, 100)});
}
static void update() { // Repeats every frame
printf("%f secs \n", deltaTime);
// Get the mouse' current state
mouse = SDL_GetMouseState(&mouseX, &mouseY);
particle.setPos(Vec2((float)mouseX, (float)mouseY));
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); // Draw Boundaries
rotator += 0.01f;
poly1.setRot(rotator);
// Player
player.Show(renderer);
square.Show(renderer);
poly1.Show(renderer);
ellipse.Show(renderer);
path.Show(renderer);
collision();
}
static void collision() {
// Clears stack
collisionStack.clear();
// Adds colliders to stack
square.makeCollider(collisionStack);
poly1.makeCollider(collisionStack);
ellipse.makeCollider(collisionStack);
path.makeCollider(collisionStack);
player.makeCollider(collisionStack);
// Draw Particles
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 100);
particle.collide(renderer, collisionStack);
}
static void movement() {
Vec2 velocity = arrowInp * moveSpeed;
}
}
I don't imagine anyone needs to see the header files, but I can provide them if requested. I also declare my start() and update() functions in GameEngine_CORE, which I'm not sure is correct, however I was getting complaints from Visual Studio that it could not find the function definitions otherwise.

SDL_RenderCopy not doing anything

I'm calling SDL_RenderCopy and it gets called and returns normally but doesn't draw anything to the window. Edited to make the question and code clearer. I'm thinking I might be trying to use something beyond its scope and hence it can't be called but this doesn't produce any error so I'm not sure. Here's the simple picture I refer to https://commons.wikimedia.org/wiki/Category:PNG_chess_pieces/Standard_transparent#/media/File:Chess_kdt60.png
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
// Recreation of the problem. Doesnt draw anything onto the white screen.
class King{
public:
King(SDL_Renderer *renderer){
SDL_Surface *Piece;
Piece = IMG_Load("Pieces/BK.png"); // I'll attach the picture
king = SDL_CreateTextureFromSurface(renderer, Piece);
SDL_FreeSurface(Piece);
kingRect.h = 100;
kingRect.w = 100;
}
~King(){}
void render(SDL_Renderer *renderer){
SDL_RenderCopy(renderer, king, NULL, &kingRect); // 99% sure the problem is this
}
private:
SDL_Texture *king;
SDL_Rect kingRect;
};
class Game {
public:
Game(const char *title, int sidelength){
isRunning = true;
if(SDL_Init(SDL_INIT_EVERYTHING) != 0) isRunning = false;
window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, sidelength, sidelength, SDL_WINDOW_OPENGL);
if(window == NULL) isRunning = false;
renderer = SDL_CreateRenderer(window, -1, 0);
if(!renderer) isRunning = false;
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
}
~Game(){}
void handleEvents(){
//Handles Events. I know this works.
}
}
void update(){};
void render(){
SDL_RenderClear(renderer);
BK.render(renderer);
SDL_RenderPresent(renderer);
}
void clean(){
//Cleans up after. I know this works.
SDL_DestroyWindow(window);
SDL_DestroyRenderer(renderer);
SDL_Quit();
}
bool running(){return(isRunning);}
King BK{renderer};
private:
bool isRunning{true};
SDL_Window *window;
SDL_Renderer *renderer;
};
Game *game = nullptr;
int main(int argc, const char *argv[]){
game = new Game("Testing Window", 800);
while(game->running()){
game->handleEvents();
game->update();
game->render();
}
game->clean();
return(0);
}
King BK{renderer}; field gets initialised before your Game::Game finishes and gets a chance to assign a renderer, so it gets NULL instead. NULL is not a valid renderer and can't create textures. If you would have checked for error you would have got Invalid renderer message. Also decent compiler with enabled warnings will tell something like warning: 'Game::renderer' is used uninitialized in this function [-Wuninitialized]; consider enabling better warning levels in your compiler.
Second thing is that you never called IMG_Init with required image formats you intend to load.
Third thing is that code is misformatted and wouldn't compile without modifications. I suggest testing code that you post as MCCVE for still being compilable and reproducing your problem (as MCCVE implies).

Garbage values when accessing SDL_Rect members from an array?

I've been following LazyFoo's SDL tutorials (and also adding my own organization and coding style). When I got to his animation tutorial I decided to make a separate class to store the variables and methods related to the animation algorithm, rather than having global variables. He uses an array of SDL_Rects to define the boundaries of different sprites on a sprite sheet, so I used an SDL_Rect pointer to store the array in my custom class. When I compiled everything I didn't see an animation, when I compiled the original source code I did. When I started debugging things, I found that when I was rendering the sprites, the rects were actually full of garbage, even though when I initialize them the rects are just fine. I've tried to simplify the problem so many times, but every approach I take to recreate the bug in a simpler environment actually works as expected! So with that in mind I apologize for the large amount of code, because I can't seem to reduce the problem.
texture.h
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL2/SDL.h>
#include <string>
class Animation {
public:
Animation(SDL_Renderer* renderer);
~Animation();
void load(std::string path, int frames, SDL_Rect* clips),
free(),
render(int x, int y),
next_frame();
private:
SDL_Renderer* _renderer=NULL;
SDL_Rect* _clips=NULL;
SDL_Texture* _texture=NULL;
int _frame=0, _frames=0, _width=0, _height=0;
};
#endif
texture.cpp
#include <stdio.h>
#include <SDL2/SDL_image.h>
#include "texture.h"
#include "error.h"
Animation::Animation(SDL_Renderer* renderer) {
_renderer = renderer;
}
Animation::~Animation() {
free();
_renderer = NULL;
}
void Animation::load(std::string path, int frames, SDL_Rect* clips) {
free();
SDL_Texture* texture = NULL;
SDL_Surface* surface = IMG_Load(path.c_str());
if (!surface)
throw ErrorIMG("Could not load image "+path);
SDL_SetColorKey(surface, SDL_TRUE,
SDL_MapRGB(surface->format, 0, 0xFF, 0xFF));
texture = SDL_CreateTextureFromSurface(_renderer, surface);
if (!texture)
throw ErrorSDL("Could not create texture from image "+path);
_width = surface->w;
_height = surface->h;
SDL_FreeSurface(surface);
_frames = frames;
_clips = clips;
printf("clips[%d]: w: %d h: %d\n", 0, _clips[0].w, _clips[0].h);
}
void Animation::free() {
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = NULL;
_clips = NULL;
_frames = 0;
_frame = 0;
_width = 0;
_height = 0;
}
}
void Animation::render(int x, int y) {
SDL_Rect crect = _clips[_frame/4];
printf("in render (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
SDL_Rect render_space = {x, y, crect.w, crect.h};
SDL_RenderCopy(_renderer, _texture, &_clips[_frame], &render_space);
}
void Animation::next_frame() {
SDL_Rect crect = _clips[_frame/4];
printf("in next frame (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
++_frame;
if (_frame/4 >= _frames)
_frame = 0;
}
game.h
#ifndef GAME_H
#define GAME_H
#include "texture.h"
class Game {
public:
Game();
~Game();
void main();
private:
void load_media();
SDL_Window* _window=NULL;
SDL_Renderer* _renderer=NULL;
Animation* _anim=NULL;
const int SCREEN_WIDTH=640, SCREEN_HEIGHT=480;
};
#endif
game.cpp
#include <SDL2/SDL_image.h>
#include "game.h"
#include "error.h"
void Game::main() {
load_media();
bool has_quit = false;
SDL_Event event;
while (!has_quit) {
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT)
has_quit = true;
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(_renderer);
_anim->render(100, 100);
_anim->next_frame();
SDL_RenderPresent(_renderer);
}
}
Game::Game() {
if (SDL_Init(SDL_INIT_VIDEO))
throw ErrorSDL("SDL could not initialize");
_window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!_window)
throw ErrorSDL("Window could not be created");
Uint32 render_flags = SDL_RENDERER_ACCELERATED;
render_flags |= SDL_RENDERER_PRESENTVSYNC;
_renderer = SDL_CreateRenderer(_window, -1, render_flags);
if (!_renderer)
throw ErrorSDL("Renderer could not be created");
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
throw ErrorIMG("SDL_image could not initialize");
}
Game::~Game() {
delete _anim;
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
_renderer = NULL;
_window = NULL;
IMG_Quit();
SDL_Quit();
}
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
for (int i=0; i < nclips; i++) {
clips[i].x = i*64;
clips[i].y = 0;
clips[i].w = 64;
clips[i].h = 164;
}
_anim = new Animation(_renderer);
_anim->load("sheet.png", nclips, &clips[0]);
}
You're storing a pointer to a temporary. The SDL_Rect* clips pointer that you pass to Animation::load is assigned to the member _clips used after the function returns. For this to work correctly, the data that is pointed to needs to live for as long as the Animation class is using it. The problem arises here:
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
...
_anim->load("sheet.png", nclips, &clips[0]);
}
In this piece of code, clips is a local variable. That means it gets destroyed at the end of load_media(), and the memory contents at that location will become garbage.
There are a number of ways you could fix this. A simple one would be to use std::vector<SDL_Rect> instead of SDL_Rect*. std::vector can safely be copied and manages its internals for you. Your new code could look like:
class Animation {
...
std::vector<SDL_Rect> _clips;
...
}
void Animation::load(std::string path, int frames, std::vector<SDL_Rect> clips) {
...
_clips = clips;
...
}
void Game::load_media() {
const int nclips = 4;
std::vector<SDL_Rect> clips;
clips.resize(nclips);
...
_anim->load("sheet.png", nclips, clips);
}
And dont forget to #include <vector>. Documentation for std::vector is here. Note that std::vector has a size() method that can probably replace frames everywhere it appears.
The stack-allocated Game::load_media()::clips array disappears when it goes out of scope. Make a copy in Animation::load() instead of only storing a pointer.

C++ IMG_LoadTexture() returns null

I'm trying to load a image("carnero.png") but when I use IMG_LoadTexture(), it returns null;
Game.h
#ifndef GAME_H_
#define GAME_H_
#include <SDL.h>
#include <SDL_image.h>
#include <windows.h>
class Game {
public:
Game();
~Game();
void run();
void initGraphics();
void gameLoop();
private:
SDL_Window* _window = nullptr;
SDL_Renderer* _renderer;
SDL_Surface* _surfaceBMP;
SDL_Texture* _textureScenario;
SDL_Texture* _textureCarnero;
SDL_Rect* _scenarioRect;
SDL_Rect* _carneroRect;
int _width;
int _height;
bool _running;
};
#endif /* SRC_GAME_H_ */
Game.cpp
#include "Game.h"
#include <iostream>
Game::Game(){
_running = true;
run();
}
Game::~Game(){
}
void Game::run(){
initGraphics();
gameLoop();
}
void Game::initGraphics(){
SDL_Init(SDL_INIT_VIDEO);
IMG_Init(IMG_INIT_PNG);
_window = SDL_CreateWindow("Carneiro", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1024, 768, SDL_WINDOW_SHOWN);
if(_window == nullptr) exit(1);
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED);
_surfaceBMP = SDL_LoadBMP("textures/scenario.bmp");
_textureScenario = SDL_CreateTextureFromSurface(_renderer, _surfaceBMP);
SDL_FreeSurface(_surfaceBMP);
_textureCarnero = IMG_LoadTexture(_renderer, "/textures/carnero2.png");
if(_textureCarnero == nullptr) exit(1);
_scenarioRect->x = 0; _scenarioRect->w = 1024;
_scenarioRect->y = 0; _scenarioRect->h = 740;
_carneroRect->x = 20; _carneroRect->w = 150;
_carneroRect->y = 100; _carneroRect->h = 100;
}
void Game::gameLoop(){
while(_running){
Sleep(10);
SDL_Event evnt;
if(SDL_PollEvent(&evnt)){
switch(evnt.type){
case SDL_QUIT:
_running = false;
break;
}
}
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _textureScenario, nullptr, _scenarioRect);
// SDL_QueryTexture(_textureCarnero, NULL, NULL, &_carneroRect->x, &_carneroRect->y);
SDL_RenderCopy(_renderer, _textureCarnero, nullptr, _carneroRect);
SDL_RenderPresent(_renderer);
}
SDL_DestroyTexture(_textureScenario);
SDL_DestroyTexture(_textureCarnero);
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
SDL_Quit();
IMG_Quit();
}
This function returns null
_textureCarnero = IMG_LoadTexture(_renderer, "/textures/carnero2.png");
But when I use SDL_LoadBMP() to load the background it works. I tried putting my .png in other folders but it doesn't work either. I also tried to load my .png using IMG_LOAD() but i had no sucess.
Your path is incorrect. /textures/carnero2.png will search for a file in C:\textures\carnero2.png, or /textures/carnero2.png on unix.
You can solve this problem as follows:
Use full (absolute) path: C:\Program Files (x86)\MyGame\textures\carnero2.png, /usr/local/share/mygame/textures/carnero2.png
Add a dot ./textures/carnero2.png
Remove the slash: textures/carnero2.png.
The path to your input file is probably incorrect:
/textures/carnero2.png
should probably be
textures/carnero2.png
like in the previous (working) load command.
In the future, I suggest that you test for file existence before trying to load the file. So you can separate "file not found" errors from real format/corrupt file problems.

SDL2: How to keep aspect ratio when resizing the window

I am trying to create a SDL window which keeps its aspect ratio when resize event happens. If user widens the window, the height is increased and vice versa. I catch the SDL_WINDOWEVENT_RESIZED event, calculate new width or height which maintains the aspect ratio and then call SDL_SetWindowSize() with calculated values.
The problem is that calling the SDL_SetWindowSize() function inside the event polling loop does nothing on the screen. SDL does update the window size variables (calling SDL_GetWindowSize() in my main loop returns the updated window dimensions). However, the actual window is not updated.
The only way I can get this to work is to call constantly SDL_SetWindowSize() in the main loop, but I think that is the wrong way of doing things. The code below illustrates my problem. Is there a better and cleaner way to get this to work?
I am using SDL 2.0.3 and 64-bit Ubuntu Linux with GNOME desktop.
#include <SDL2/SDL.h>
static const float ASPECT_RATIO = 16.f/9.f;
SDL_Window* window;
SDL_Renderer* renderer;
uint32_t windowID;
SDL_Rect screen;
bool done = false;
bool resizeDone = false;
void handle_events()
{
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_WINDOWEVENT:
if(e.window.windowID == windowID) {
switch(e.window.event) {
case SDL_WINDOWEVENT_RESIZED: {
int width = e.window.data1;
int height = e.window.data2;
float aspectRatio = (float)width/(float)height;
if(aspectRatio != ASPECT_RATIO) {
if(aspectRatio > ASPECT_RATIO) {
height = (1.f / ASPECT_RATIO) * width;
}
else {
width = ASPECT_RATIO * height;
}
printf("Setting window size to %d, %d, aspect ratio: %f\n",
width, height, (float)width/(float)height);
}
screen.w = width;
screen.h = height;
SDL_SetWindowSize(window, width, height); // <-- does not work
resizeDone = true;
break;
}
}
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
}
void run() {
while(!done) {
//SDL_SetWindowSize(window, screen.w, screen.h); // <-- works
handle_events();
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
if(resizeDone) {
int w, h;
SDL_GetWindowSize(window, &w, &h);
printf("SDL_GetWindowSize: %d, %d\n", w, h);
resizeDone = false;
}
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
uint32_t window_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_RESIZABLE;
window = SDL_CreateWindow("Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);
windowID = SDL_GetWindowID(window);
renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
run();
SDL_Quit();
return 0;
}
Some window managers seems to ignore resize requests made while WM itself resizes window (e.g. while mouse button held). On contrary, SDL_GetWindowSize returns cached values, which in that specific case sometimes happens to be wrong.
I see no platform-independent way to achieve that, other than constantly calling SDL_SetWindowSize on each frame, just in case. It could be achieved using platform-specific APIs, though (like SDL_GetWindowSysWMInfo and then using Xlib).
On macOS, I have solved it like this:
cocoa.m:
#import <Cocoa/Cocoa.h>
void SetWindowRatio(void *window) {
NSWindow *win = (__bridge NSWindow*) window;
win.aspectRatio = NSMakeSize( 1280, 720 );
}
main.cpp:
#include <SDL.h>
#include <SDL_syswm.h>
extern "C" void SetWindowRatio(void *window);
// and later..
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
SDL_GetWindowWMInfo(sdl.window, &wmInfo);
SetWindowRatio(wmInfo.info.cocoa.window);
Perhaps something similar could be done on Linux, only access different part of wmInfo.info. and call the native function?