I am attempting to code a basic game engine in c++/SDL. But while testing my first class, I get memory errors: or Segmentation Fault
s, or double free or corruption
.
It always crashes in the same way, but than the different errors seem to occur at random.
To demonstrate, compile my source code and execute it with as argument the path to a .bmp
image file. When you left click, it adds the sprite to the screen on the position your mouse was. when you right click, any sprite in contact with the one on the mouse is supposed to get erased. But instead, it crashes.
I searched a bit/a bit more/a lot more, looked for issues such as deep vs shallow copy for the pointers used, an so on, but without success.
This question is only related in the fact that I am using a dynamic array.
My source, all in one file for your convenience (sorry its around 150 lines, I tried to strip it as much as I could and commented it as much as possible :-/ ):
#include <iostream> //system includes
#include <string>
#include <vector>
#include <SDL/SDL.h> //lib includes
//#include "Tile.h" //project includes
class Tile //-------------------------- TILE.H CONTENT ----------------------
{
public:
Tile(const std::string &imgFile, const int &x, const int &y);
Tile(const Tile &tile);
void draw(SDL_Surface* dest) const;
void setPos(const int &x, const int &y);
bool coliding(const Tile &tile) const;
virtual ~Tile();
protected:
private:
void init(const std::string &imgFile, const int &x, const int &y); //helper func
SDL_Surface* m_img;
SDL_Rect* m_rect;
std::string m_imgFile; //for deep copying
};
Tile::Tile(const Tile &tile) // ----------------------- TILE.CPP CONTENT -------------------------
{
init(tile.m_imgFile/*This is to deep copy th img*/, tile.m_rect->x, tile.m_rect->y);
}
Tile::Tile(const std::string &imgFile, const int &x, const int &y)
{
init(imgFile, x, y);
}
void Tile::init(const std::string &imgFile, const int &x, const int &y)
{
m_img = SDL_DisplayFormat(SDL_LoadBMP(imgFile.c_str())); // load and optimize sprite
m_imgFile = imgFile;
if (!m_img)
{
std::cout << "Unable to load image: " << SDL_GetError() << std::endl;
exit(EXIT_FAILURE);
}
m_rect = new SDL_Rect;
m_rect->h = m_img->h;
m_rect->w = m_img->w;
setPos(x, y);
}
void Tile::draw(SDL_Surface* dest) const
{
SDL_BlitSurface(m_img, 0, dest, m_rect);
}
void Tile::setPos(const int &x, const int &y)
{
m_rect->x = x;
m_rect->y = y;
}
bool Tile::coliding(const Tile &tile) const
{
int x1 = m_rect->x;
int y1 = m_rect->y;
int x2 = m_rect->x + m_rect->w;
int y2 = m_rect->y + m_rect->h;
int x3 = tile.m_rect->x;
int y3 = tile.m_rect->y;
int x4 = tile.m_rect->x + tile.m_rect->w;
int y4 = tile.m_rect->y + tile.m_rect->h;
if ((((x1 >= x3 and x1 <= x4) and (y1 >= y3 and y1 <= y4)) or
((x2 >= x3 and x2 <= x4) and (y2 >= y3 and y2 <= y4)) or
((x1 >= x3 and x1 <= x4) and (y2 >= y3 and y2 <= y4)) or
((x2 >= x3 and x2 <= x4) and (y1 >= y3 and y1 <= y4)))
or
(((x3 >= x1 and x3 <= x2) and (y3 >= y1 and y3 <= y2)) or
((x4 >= x1 and x4 <= x2) and (y4 >= y1 and y4 <= y2)) or
((x3 >= x1 and x3 <= x2) and (y4 >= y1 and y4 <= y2)) or
((x4 >= x1 and x4 <= x2) and (y3 >= y1 and y3 <= y2))))
return(true);
else return(false);
}
Tile::~Tile()
{
if(m_img != NULL)
{
SDL_FreeSurface(m_img);
m_img = NULL;
}
if(m_rect != NULL)
{
delete m_rect;
m_rect = NULL;
}
//dtor
}
using namespace std;
int main (int argc, char **argv) //---------------------- MAIN.CPP ------------------------
{
if(argc != 2) //prog name + arg
{
cout << "Needs EXACTLY one argument: file for sprite.\n";
return EXIT_FAILURE;
}
// initialize SDL video
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
cout << "Unable to init SDL: " << SDL_GetError() << endl;
return EXIT_FAILURE;
}
// make sure SDL cleans up before exit
atexit(SDL_Quit);
// create a new window
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 16,
SDL_HWSURFACE|SDL_DOUBLEBUF);
if ( !screen ) //if screen is a NULL pointer:
{
cout << "Unable to set 640x480 video: " << SDL_GetError() << endl;
return EXIT_FAILURE;
}
//create "mouse" sprite and container for the others
vector<Tile> tiles;
Tile tile(argv[1], 0, 0);
// program main loop
bool done = false;
bool click_l = false;
bool click_r = false;
while (!done)
{
// message processing loop
SDL_Event event;
while (SDL_PollEvent(&event))
{
// check for messages
switch (event.type)
{
// exit if the window is closed
case SDL_QUIT:
done = true;
break;
// check for keypresses
case SDL_KEYDOWN:
{
switch(event.key.keysym.sym) //if its a key and its...
{
case SDLK_ESCAPE: //escape: exit
done = true;
break;
case SDLK_r: //r: empty screen
tiles.clear();
break;
default: //something else: do nothing
break;
} //end of key switch
break;
}
case SDL_MOUSEMOTION: //if the mouse moves
tile.setPos(event.motion.x, event.motion.y); //move the mouse tile to it
break;
case SDL_MOUSEBUTTONDOWN: //update click bools
if(event.button.button == SDL_BUTTON_LEFT)
click_l = true;
else if(event.button.button == SDL_BUTTON_RIGHT)
click_r = true;
break;
case SDL_MOUSEBUTTONUP://update click bools
if(event.button.button == SDL_BUTTON_LEFT)
click_l = false;
else if(event.button.button == SDL_BUTTON_RIGHT)
click_r = false;
break;
} // end switch
} // end of message processing
if(click_l) //add a tile on screen if left click
{
tiles.push_back(tile);
}
else if(tiles.size()>=1 and click_r) //else if right click
{
for(size_t i = 0; i < tiles.size(); i++) //go through container
{
if(tiles[i].coliding(tile)) //if a tile is coliding with mouse
{
tiles.erase(tiles.begin() + i--); //delete it, decrement i to avoid skiping a tile
}
}
}
// DRAWING STARTS HERE
// clear screen
SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 255, 255, 255));
// draw bitmaps
if(tiles.size()>=1) //draw all tiles in conatainer
{
for(int i=tiles.size(); i-- > 0;)
{
tiles[i].draw(screen);
}
}
tile.draw(screen); //draw tile on mouse
// DRAWING ENDS HERE
// finally, update the screen :)
SDL_Flip(screen);
} // end main loop
// if reaches here, no error occured
cout << endl << "Exited cleanly" << endl;
return EXIT_SUCCESS;
}
Press r
to empty the screen (That works without crashing, for some reason).
Thank you !