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.
Related
I was making a little program to practice pointer usage and rendering.
I was trying to debug an Illegal memory access, and my program crashed.
The problem, is that it crashed and I had to force-quit the debugging session by pressing shift-f5
because my program was taking mouse focus, and I could not click anything.
Now my screen is constantly brighter than before, probably due to an draw operation that was supposed to draw a fullscreen white rectangle.
This will probably be fixed when I restart my PC.... but it is still a problem if this stacks, since I will have to restart every 3 debugging sessions to not burn my eyes.
Any advice on how to ENSURE my program releases resources?
Edit for reproducible example:
#include "SDL.h"
#include "SDL_render.h"
#include <stdio.h>
#include <iostream>
#include <list>
#include <string>
#include <ft2build.h>
#include <SDL_ttf.h>
#include <functional>
#include <memory>
#include FT_FREETYPE_H
struct element {
SDL_Rect* area;
SDL_Texture* texture;
SDL_Surface* surface;
std::string name = "";
std::function<void()> functionPointer;
element(SDL_Rect* rect, SDL_Texture* tex, SDL_Surface* surf, std::string elementName, std::function<void()> fp) {
area = rect;
texture = tex;
surface = surf;
name = elementName;
functionPointer = fp;
}
~element() {
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
area = nullptr;
surface = nullptr;
texture = nullptr;
name = "";
functionPointer = nullptr;
}
};
struct screenVariables {
std::list<SDL_Texture*> textureList;
std::list<element*>& elementList;
SDL_Renderer* renderer;
std::list<SDL_Surface*> surfaceList;
screenVariables(std::list<SDL_Texture*> tex, std::list<element*> rec, SDL_Renderer* render,std::list<SDL_Surface*> surf) :
textureList(tex),elementList(rec), renderer(render),surfaceList(surf) {};
};
class gameHandler{
private:
SDL_Window* screen;
SDL_Renderer* renderer;
SDL_Texture* mainBlock;
bool saveAvailable = false;
bool quit = false;
std::list<SDL_Texture*> textureList;
std::list<element*> elementList;
std::list<SDL_Surface*> surfaceList;
screenVariables screenInfo{ textureList, elementList, renderer, surfaceList };
void nothing() {};
void continuePressed() {};
void newGame() {};
public:
gameHandler() {
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS);
TTF_Init();
screen = SDL_CreateWindow("Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1920, 1080,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
renderer = SDL_CreateRenderer(screen, -1, 0);
mainBlock = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
SDL_TEXTUREACCESS_TARGET, 1920, 1080);
}
//All methods that do operations on elements will operate on the LAST ADDED ELEMENT. Always create a new element and use push_back() to add it to the end.
//Everything commented out is not nessesary for reproduction, but
//will be left for reference.
/*bool showFirstElement(screenVariables& arg, const char* imgPath, SDL_Rect* area) {
SDL_Renderer* renderRef = arg.renderer;
//The following line means: end() returns an iterator, take the iterator and make it go back once, then deference it to get the value.
//This gets the value of the last added surface.
SDL_Surface* newSurface = *(--(arg.surfaceList.end()));
newSurface = SDL_LoadBMP(imgPath);
SDL_Texture* newTexture = *(--(arg.textureList.end()));
newTexture = SDL_CreateTextureFromSurface(renderRef, newSurface);
std::function<void()> nothingWrapper = [&]() { nothing(); };
element* toBeModified = *(--(arg.elementList.end()));
toBeModified->element::area = area;
toBeModified->texture = newTexture;
toBeModified->surface = newSurface;
toBeModified->name = imgPath;
toBeModified->functionPointer = nothingWrapper;
arg.elementList.push_back(toBeModified);
SDL_RenderCopy(renderRef, newTexture, NULL, area);
return true;
}
bool textOnElement(screenVariables& arg, const char* text,int points, element* el) {
SDL_Renderer* renderRef = arg.renderer;
TTF_Font* font = NULL;
SDL_Color textColor = { 0,0,0 };
font = TTF_OpenFont("TravelingTypewriter.ttf", points);
SDL_Surface* newSurface = *(--(arg.surfaceList.end()));
newSurface = TTF_RenderText_Solid(font,text,textColor);
SDL_Texture* newTexture = *(--(arg.textureList.end()));
newTexture = SDL_CreateTextureFromSurface(renderRef, newSurface);
el->surface = newSurface;
el->texture = newTexture;
SDL_RenderCopy(renderRef, newTexture, NULL, el->area);
return true;
}
element* checkClickedElement(screenVariables& arg, SDL_Point pos) {
for (element* i : arg.elementList) {
if (SDL_PointInRect(&pos, i->area)) {
return i;
}
return nullptr;
}
}
bool fillWithColor(screenVariables& arg, Uint32 red, Uint32 green, Uint32 blue, Uint32 alpha) {
SDL_Renderer* renderRef = arg.renderer;
SDL_Surface* surface = SDL_CreateRGBSurface(0, 1920, 1080, 8, red, green, blue, alpha);
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderRef, surface);
SDL_Rect fullScreen = { 1920,1080 };
std::string name = "R" + std::to_string(red) + "G" + std::to_string(green) + "B" + std::to_string(blue);
std::function<void()> nothingWrapper = [&]() { nothing(); };
element newElement = element(&fullScreen, texture, surface, name, nothingWrapper);
SDL_RenderCopy(renderRef, texture, NULL, NULL);
return true;
}
bool fillElementPointer(screenVariables& arg, SDL_Point leftTop, SDL_Point rightBottom, std::function<void()> fp,std::string name,element* newElement) {
SDL_Rect newRect;
newRect.x = (leftTop.x + rightBottom.x) / 2;
newRect.y = (leftTop.y + rightBottom.y) / 2;
newRect.w = (rightBottom.x - leftTop.x);
newRect.h = (leftTop.y - rightBottom.y);
newElement = &element(&newRect, nullptr, nullptr, name, fp);
arg.elementList.push_back(newElement);
return true;
}
//This allows me to create pointers that will not go out of scope when methods return. Always use this method if the element you will create needs to presist through functions.
element* createElementPointer(){
element* newPointer = nullptr;
return newPointer;
}
int showMenu(screenVariables& arg) {
fillWithColor(arg, 255, 255, 255, 255);
SDL_Point leftTop = { 200,700 };
SDL_Point rightBottom = { 600,200 };
std::function<void()> nothingWrapper = [&]() { nothing(); };
element* titleText = createElementPointer();
if (!fillElementPointer(screenInfo, leftTop, rightBottom, nothingWrapper, "titleText", titleText)) {
std::cout << "createElement failed in showMenu()";
}
leftTop.y = 100;
rightBottom.x = 0;
element* continueOrStart = createElementPointer();
if (saveAvailable) {
std::function<void()> continueWrapper = [&]() { continuePressed(); };
if (!fillElementPointer(screenInfo, leftTop, rightBottom, continueWrapper, "continueButton", continueOrStart)) {
std::cout << "createElement failed in showMenu() saveAvailable.";
}
textOnElement(arg, "Continue", 16, continueOrStart);
}
else {
std::function<void()> newGameWrapper = [&]() { newGame(); };
if (!fillElementPointer(screenInfo, leftTop, rightBottom, newGameWrapper, "newGameButton", continueOrStart)) {
std::cout << "createElement failed in showMenu() !saveAvailable.";
}
textOnElement(arg, "New Game", 16, continueOrStart);
}
return 0;
}
void mainLoop() {
SDL_Event event;
showMenu(screenInfo);
while (!quit) {
SDL_RenderClear(renderer);
while (SDL_PollEvent(&event) != 0) {
switch (event.type)
{
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
SDL_Point position = { event.button.x,event.button.y };
element* result = checkClickedElement(screenInfo, position);
if (result == nullptr) {
break;
}
else {
result->functionPointer();
}
}
break;
case SDL_QUIT:
quit = true;
break;
}
}
SDL_RenderPresent(renderer);
}
}
void clearResources() {
int index{ 0 };
for (element* i : screenInfo.elementList) {
delete i;
}
SDL_DestroyRenderer(renderer);
SDL_Quit();
} */
};
int main(int argc, char* argv[]) {
gameHandler handler; //This alone makes my screen brighter.
//Uncomment every function and let mainLoop() run, and it crashes.
//Although many attempts at making this exception safe, it did not work.
//handler.mainLoop();
return 0;
}
I'm trying to make a simple Snake game. The self collision and a lot of things aren't ready. The problem is: Even with no errors, running just fine, changing "close()" to "endGame()" and reverting, rebuilding the entire program, i could not render anything with SDL_RenderCopy. You will see many unnecessary things in my code and some Brazilian-Portuguese comments, prepare yourself.
The image is a 16x16 png spritesheet, using the color #ff00ff as ColorKey. There are only 4 sprites in this spritesheet, respectively: Apple, Snake's Body, Snake's Head and Snake's Tail (still unused).
Whole code:
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
using namespace std;
const int CELL_SIZE = 16;
const int CELL_WIDTH = 16;
const int CELL_HEIGHT = 16;
const int SCREEN_WIDTH = CELL_SIZE * (CELL_WIDTH-1);
const int SCREEN_HEIGHT = CELL_SIZE * (CELL_HEIGHT-1);
SDL_Window* gWindow = NULL;
SDL_Renderer* gRenderer = NULL;
void loadTexture(string path);
SDL_Texture* spriteSheet = NULL;
void loadMedia();
void init();
class Snake {
int initialLength = 3; //from body (head incluse)
int length = initialLength;
int facing = DIR_UP;
//Head position in CELLS (multiplied for CELL_SIZE when drawed)
int x = 5;
int y = 5;
//UNUSED TEMPORARY COLORS - IGNORE
Uint8 color[3] = {0x22,0x88,0x44}; //Snake Color, em RGB
Uint8 headColor[3] = {0x11,0x44,0x44}; //Color for the Head of the Snek
int stepDelay = 60; //time in frames that the Snakes waits till move
int stepFramesRemaining = stepDelay;
int stepDelayReduction = 1; //reduces stepDelay every time the Snakes eats the apple
const int minStepDelay = 15; //the minimum delay
//For clipping the image (its an 16x16 png image)
const SDL_Rect clipHead = {0,8,8,8};
const SDL_Rect clipBody = {8,0,8,8};
const SDL_Rect clipTail = {8,8,8,8};
public:
enum direction { //direções nas quais a cobra pode se mover
DIR_UP,
DIR_RIGHT,
DIR_DOWN,
DIR_LEFT
};
/* The following var stores the entire Snake's body in an 2D Array. The number stored means how much "steps" it will survive.
When the value is zero, it means that there is no body in the place. The bodypart with value equivalent to "length" is the Head.
*/
int body[CELL_WIDTH][CELL_HEIGHT];
void init(); //initializes some vars
void tick(); //tick function
void stepFoward(); //moves Snake foward
void faceTo(int dir); //alters which direction Snake is faced
void eat(); //eats the apple
void draw(); //renders (or at least tries) to render the snake
int getLength(){
return length;
};
int getFacing(){
return facing;
};
} Snake;
class Food {
Uint8 color[3] = {0x85,0x22,0x10}; //RGB color of the Apple
bool visible = true;
public:
int x = 2;
int y = 2;
void respawn();
void destroy();
void draw();
} Food;
void Food::respawn(){
//teleports the apple to a random spot
x = rand() % (CELL_WIDTH-2);
y = rand() % (CELL_HEIGHT-2);
visible = true;
}
void Food::destroy(){
//Reset values
x = 0;
y = 0;
visible = false;
//resets
respawn();
}
void Food::draw(){
if(visible){
SDL_Rect rect = {x*CELL_SIZE,y*CELL_SIZE,CELL_SIZE,CELL_SIZE};
SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_RenderFillRect(gRenderer, &rect);
}
}
void Snake::init(){
//Spawns in a vertical line
for(int i=0; i<length; i++){
body[x][y+i] = length-i;
}
}
void Snake::tick(){
if(stepFramesRemaining > 0){
stepFramesRemaining--;
} else {
//when 0, moves the snake
stepFramesRemaining = stepDelay;
stepFoward();
}
}
void Snake::eat(){
//increases the body size by 1
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] > 0){
body[j][i]++;
}
}
}
length++;
if(stepDelay > minStepDelay){
stepDelay -= stepDelayReduction;
}
Food.destroy();
}
void Snake::draw(){
//SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_Rect rect = {0,0,0,0}; //for later use
//Draws the body and head
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] == length){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
SDL_SetRenderDrawColor(gRenderer,0x33,0xff,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
SDL_RenderCopy(gRenderer, spriteSheet, &clipHead, &rect);
} else if (body[j][i] > 0){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
//SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_RenderCopyEx(gRenderer, spriteSheet, &clipBody, &rect, 0, NULL, SDL_FLIP_NONE);
SDL_SetRenderDrawColor(gRenderer,0x66,0xee,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
}
SDL_RenderFillRect(gRenderer,&rect);
}
}
//SDL_RenderFillRect(gRenderer,&rect);
}
void Snake::stepFoward(){
int headMoved = 0; //informs if the head already moved
//decreases the "body" lifespan and moves head
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] > 0){
//Verifica se é a cabeça, para movê-la logo em seguida
if(body[j][i] == length && headMoved < 2){
//moves head position, looping if needed
switch(facing){
case DIR_UP:
if(y == 0){
body[x][CELL_HEIGHT-1] = length;
y = CELL_HEIGHT-1;
} else {
body[x][y-1] = length;
y--;
}
break;
case DIR_DOWN:
if(y == CELL_HEIGHT-2){
body[x][0] = length+1; //(+1 to avoid being subtracted twice)
y = 0;
} else {
body[x][y+1] = length+1;
y++;
}
break;
case DIR_LEFT:
if(x == 0){
body[CELL_WIDTH-1][y] = length;
x = CELL_WIDTH-1;
} else {
body[x-1][y] = length;
x--;
}
break;
case DIR_RIGHT:
if(x == CELL_WIDTH-2){
body[0][y] = length+1; //avoiding again the "-2" subtraction.
x = 0;
} else {
body[x+1][y] = length+1;
x++;
}
break;
}
headMoved++;
}
body[j][i]--; //decreases the "body" lifespan
}
}
}
//verifies if can eat (head in the same position as the apple)
if(x == Food.x && y == Food.y){
eat();
}
}
void Snake::faceTo(int dir){
facing = dir;
}
void init();
void close();
void init(){ //Initializes the game
gWindow = SDL_CreateWindow("· Snake ·", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );
if( gRenderer == NULL ){
printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError() );
}
int imgFlags = IMG_INIT_PNG;
if(!(IMG_Init(imgFlags) & imgFlags)){
cout << "IMG INIT error!" << endl;
}
loadMedia();
Snake.init();
}
void close(){ //Closes the program
SDL_DestroyTexture(spriteSheet);
spriteSheet = NULL;
SDL_DestroyRenderer(gRenderer);
gRenderer = NULL;
IMG_Quit();
SDL_Quit();
}
void loadTexture(string path){ //Almost the same function from LazyFoo tutorial
//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
{
//Color key image
SDL_SetColorKey( loadedSurface, SDL_TRUE, SDL_MapRGB( loadedSurface->format, 0xff, 0x00, 0xff));
//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() );
}
//Get rid of old loaded surface
SDL_FreeSurface( loadedSurface );
}
spriteSheet = newTexture;
}
void loadMedia(){ //loads everything (it will load sound too, when i make the sounds of course)
loadTexture("spritesheet.png");
if(spriteSheet == NULL){
cout << "ERRO" << endl;
}
}
void tick(){
Snake.tick();
}
void draw(){ //Render Function
//Background
SDL_SetRenderDrawColor(gRenderer, 0xee, 0xf2, 0xf0, 0xff);
SDL_RenderClear(gRenderer);
Snake.draw();
Food.draw();
//Aplica as alterações
SDL_RenderPresent(gRenderer);
}
int main(){
srand (time (NULL));
init();
bool quit = false;
SDL_Event e; //Event Handling
while(!quit){
while(SDL_PollEvent(&e) != 0){
if(e.type == SDL_QUIT){
quit = true;
} else if(e.type == SDL_KEYDOWN){
switch(e.key.keysym.sym){
case SDLK_UP:
Snake.faceTo(Snake.DIR_UP);
break;
case SDLK_DOWN:
Snake.faceTo(Snake.DIR_DOWN);
break;
case SDLK_LEFT:
Snake.faceTo(Snake.DIR_LEFT);
break;
case SDLK_RIGHT:
Snake.faceTo(Snake.DIR_RIGHT);
break;
}
}
}
//Tick function
tick();
//Renders everything
draw();
SDL_RenderCopy(gRenderer, spriteSheet, NULL, NULL);
//Slows down the program just a bit (its not a time based frame system... yet)
SDL_Delay(3);
}
close(); //ends
return 0;
}
The portion of code who should be working, but isn't:
//Draws the body and head
for(int i=0; i<CELL_HEIGHT; i++){
for(int j=0; j<CELL_WIDTH; j++){
if(body[j][i] == length){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
SDL_SetRenderDrawColor(gRenderer,0x33,0xff,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
SDL_RenderCopy(gRenderer, spriteSheet, &clipHead, &rect);
} else if (body[j][i] > 0){
rect = {j*CELL_SIZE,i*CELL_SIZE,CELL_SIZE,CELL_SIZE};
//SDL_SetRenderDrawColor(gRenderer,color[0],color[1],color[2],0xff);
SDL_RenderCopyEx(gRenderer, spriteSheet, &clipBody, &rect, 0, NULL, SDL_FLIP_NONE);
SDL_SetRenderDrawColor(gRenderer,0x66,0xee,0x22,0xff);
SDL_RenderFillRect(gRenderer,&rect);
}
SDL_RenderFillRect(gRenderer,&rect);
}
}
//SDL_RenderFillRect(gRenderer,&rect);
}
Recently I have been trying to make a button using the lazyfoo.net tutorials. The button example they code didn't fit what I needed (especially since I modified the LTexture class) so I modified it... and, of course, it didn't work.
So here is the modified LTexture class(now dubbed Texture):
#pragma once
//Using SDL, SDL_image, standard IO, and strings
#include <xstring>
#include <SDL.h> //SDL header file
#include <SDL_image.h> //SDL image file
#include <stdio.h> //standard C ouput
#include <string> //standard c++ string
#include <map>
#include <SDL_ttf.h>
using namespace std;
typedef Uint8 u8;
//Texture wrapper class (originally from lazyfoo.net)
class Texture
{
public:
//Initializes variables
Texture();
//Deallocates memory
~Texture();
//Loads image at specified path
bool loadFromFile(std::string path);
//Deallocates texture
void free();
//Renders texture at given point
void render();
//Sets the size of the image
void setSize(int width, int height);
//Adds a clip to the clips map
void addClip(string name, SDL_Rect* aclip);
//Sets the clip
void setClip(string name);
void setNullClip();
//Sets the placement of the object
void setPos(int newx, int newy);
//Moves the position of the object
void movePos(int addx, int addy);
//Gets image dimensions
int getWidth();
int getHeight();
//Sets the color
void setColor(Uint8 red, u8 green, u8 blue);
//Set blending
void setBlendMode(SDL_BlendMode blending);
//Set alpha modulation
void setAlpha(Uint8 alpha);
//Creates image from font string
bool loadFromRenderedText(std::string textureText, SDL_Color textColor);
protected:
//The actual hardware texture
SDL_Texture* mTexture;
//Image dimensions
int mWidth;
int mHeight;
//Image bottom left coordinates
int x;
int y;
//Current clip of image
SDL_Rect* clip;
//Available image clips
map<string, SDL_Rect*> clips;
};
And the function definitions:
#include "Texture.h"
#include "Univar.h"
extern SDL_Renderer* Renderer;
extern TTF_Font* Font;
Texture::Texture()
{
//Initialize
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
Texture::~Texture()
{
//Deallocate
free();
}
bool Texture::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
{
//Color key image
SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0, 0xFF, 0xFF));
//Create texture from surface pixels
newTexture = SDL_CreateTextureFromSurface(Renderer, 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 Texture::free()
{
//Free texture if it exists
if (mTexture != NULL)
{
SDL_DestroyTexture(mTexture);
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
}
void Texture::render()
{
//Set rendering space and render to screen
SDL_Rect renderQuad = { x, y, mWidth, mHeight };
//Set clip rendering dimensions
if (clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
SDL_RenderCopy(Renderer, mTexture, clip, &renderQuad);
}
void Texture::setSize(int width, int height)
{
mWidth = width;
mHeight = height;
}
void Texture::addClip(string name, SDL_Rect * aclip)
{
clips[name] = aclip;
}
void Texture::setClip(string name)
{
clip = clips[name];
}
void Texture::setNullClip()
{
clip = NULL;
}
void Texture::setPos(int newx, int newy)
{
x = newx;
y = newy;
}
void Texture::movePos(int addx, int addy)
{
x += x;
y += y;
}
int Texture::getWidth()
{
return mWidth;
}
int Texture::getHeight()
{
return mHeight;
}
void Texture::setColor(Uint8 red, u8 green, u8 blue)
{
//Modulate texture
SDL_SetTextureColorMod(mTexture, red, green, blue);
}
void Texture::setBlendMode(SDL_BlendMode blending)
{
//Set blending function
SDL_SetTextureBlendMode(mTexture, blending);
}
void Texture::setAlpha(Uint8 alpha)
{
//Modulate texture alpha
SDL_SetTextureAlphaMod(mTexture, alpha);
}
bool Texture::loadFromRenderedText(std::string textureText, SDL_Color textColor)
{
//Get rid of preexisting texture
free();
//Render text surface
SDL_Surface* textSurface = TTF_RenderText_Solid(Font, textureText.c_str(), textColor);
if (textSurface == NULL)
{
printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError());
}
else
{
//Create texture from surface pixels
mTexture = SDL_CreateTextureFromSurface(Renderer, textSurface);
if (mTexture == NULL)
{
printf("Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError());
}
else
{
//Get image dimensions
mWidth = textSurface->w;
mHeight = textSurface->h;
}
//Get rid of old surface
SDL_FreeSurface(textSurface);
}
//Return success
return mTexture != NULL;
}
Then here is the button class:
#pragma once
#include "Texture.h"
enum MouseState {
Out,
Hover
};
enum Press {
LClick,
RClick,
None
};
class Button : public Texture{
int w;
int h;
int x;
int y;
public:
//Inits the variables
Button(int aw, int ah);
//Handles events
Press handleEvent(SDL_Event* e);
};
And the function definitions:
#include "Button.h"
Button::Button(int aw, int ah) : w(aw), h(ah)
{
setSize(aw, ah);
}
Press Button::handleEvent(SDL_Event * e)
{
//If mouse event happened
if (e->type == SDL_MOUSEMOTION || e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP)
{
//Get mouse position
int ax, ay;
SDL_GetMouseState(&ax, &ay);
//Check if mouse is in button
bool inside = true;
//Mouse is left of the button
if (ax < x)
{
inside = false;
}
//Mouse is right of the button
else if (ax > x + w)
{
inside = false;
}
//Mouse above the button
else if (ay < y)
{
inside = false;
}
//Mouse below the button
else if (ay > y + h)
{
inside = false;
}
//Mouse is outside button
if (!inside)
{
setClip("out");
return None;
}
//Mouse is inside button
else
{
setClip("Hover");
//Set mouse over sprite
switch (e->button.button)
{
case SDL_BUTTON_LEFT:
return LClick;
break;
case SDL_BUTTON_RIGHT:
return RClick;
break;
default:
return None;
break;
}
}
}
else {
return None;
}
}
And when I run this, I get nothing (well I get a window but without the button in it)! If the button class seams barebones, that's because I'm pretty sure I've narrowed down the error to handleEvent(). I looked through it and couldn't find the error.
I figured out the error. The reason is I'm not used to SDL's coordinate system.
In SDL, the top left is 0,0.
In regular coordinate grids, the bottom left is 0,0.
:(P
For some strange reason whenever I try to split a larger image file into (in my case) a 32x32 picture, but instead I get the entire picture that seems to be squished down into 32x32(the picture as a whole). Although after playing around with the values I realized that for some reason the SDL has completely ignored my request to use the SDL_Rect source(src).
Meaning that every value that I change the source(src) rectangle to doesn't change the actual image when I run the program (even a ridiculous value).
Sprite.h
#ifndef SPRITE_H_
#define SPRITE_H_
#pragma once
#include "Game.h"
struct Game;
struct Sprite
{
//Needs to be fixed :/
//Sprite(const std::string& filepath,int x,int y,int width, int height,Game*game);
Sprite();
~Sprite();
void Load(const std::string& filepath, int x, int y,int width,int height, Game*game);
void Draw(Game*game);
SDL_Rect*getDstRect(){ return &dst; }
SDL_Rect*getSrcRect(){ return &src; }
private:
SDL_Surface*surface = NULL;
SDL_Texture*texture = NULL;
SDL_Rect src;
SDL_Rect dst;
int img_width;
int img_height;
};
#endif // SPRITE_H_
Sprite.cpp
#include "Sprite.h"
Sprite::Sprite()
{
}
Sprite::~Sprite()
{
SDL_DestroyTexture(texture);
surface = NULL;
texture = NULL;
}
void Sprite::Load(const std::string& filepath, int x, int y, int width, int height, Game*game)
{
bool success = true;
surface = IMG_Load(filepath.c_str());
if (surface == NULL)
{
game->getConsole()->Error("SPRITE::Surface Is Not Loaded");
success = false;
}
else
game->getConsole()->Text("SPRITE::Surface Is Loaded");
texture = SDL_CreateTextureFromSurface(game->getRenderer(), surface);
if (texture == NULL)
{
game->getConsole()->Error("SPRITE::Texture Is Not Loaded");
success = false;
}
else
game->getConsole()->Text("SPRITE::Texture is Loaded");
dst.x = x;
dst.y = y;
dst.w = height;
dst.h = height;
src.x = 0;
src.y = 0;
src.w = 0;
src.h = 0;
SDL_QueryTexture(texture, NULL, NULL, &src.w, &src.h);
SDL_FreeSurface(surface);
if (success)
game->getConsole()->Text("Loaded Sprite at " + filepath);
else
game->getConsole()->Error("SPRITE::Failed To Load Sprite at " + filepath);
}
void Sprite::Draw(Game*game)
{
SDL_RenderCopy(game->getRenderer(), texture, &src,&dst);
}
Part of the Map Class
testPlayer.Load("bin/sprites/player.png", 200,200,32,32, game);
This is where I have tried to change the values of the destination(dst) rectangle after messing with the source(src) rectangle.
https://wiki.libsdl.org/SDL_RenderCopy
dstrect - the destination SDL_Rect structure or NULL for the entire rendering target. The texture will be stretched to fill the given rectangle.
Short version - adjust source rect to have the same width and height as destination rect, and {x, y} to have starting position of fragment you want to extract.
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().