Hi.
For those unfamiliar with the program, today we will implement support for
Tiled into our C++ program.
We will use
TmxParser for this task.
The GameObjectWe will use a component based design just for the sake of it, and to make the people here happy. This is some boilerplate that you need:
namespace GameObject
{
class AComponent
{
public:
typedef std::vector<AComponent*> ComponentContainer;
private:
static ComponentContainer all;
public:
static ComponentContainer getAll() { return all; }
virtual void update() {}
AComponent()
{
all.push_back(this);
}
~AComponent()
{
auto i = std::find(all.begin(), all.end(), this);
if (i != all.end())
{
all.erase(i);
}
}
};
AComponent::ComponentContainer AComponent::all;
class PositionComponent : public AComponent
{
private:
int x;
int y;
public:
int getX() const { return x; }
int getY() const { return y; }
void setX(const int v) { x = v; }
void setY(const int v) { y = v; }
PositionComponent() :
x(0),
y(0)
{
}
PositionComponent(const int x, const int y) :
x(x),
y(y)
{
}
};
class SizeComponent : public AComponent
{
private:
int w;
int h;
public:
int getW() const { return w; }
int getH() const { return h; }
void setW(const int v) { w = v; }
void setH(const int v) { h = v; }
SizeComponent() :
w(0),
h(0)
{
}
SizeComponent(const int w, const int h) :
w(w),
h(h)
{
}
};
}
And for the graphics:
namespace GameObject
{
class GraphicComponent
{
public:
typedef std::vector<GraphicComponent*> GraphicComponentContainer;
private:
static GraphicComponentContainer all;
int sprite_number;
Video::SpriteSheet& spritesheet;
PositionComponent& position;
SizeComponent& size;
public:
static GraphicComponentContainer getAll() { return all; }
int getSpriteNumber() const { return sprite_number; }
Video::SpriteSheet& getSpritesheet() const { return spritesheet; }
PositionComponent getPosition() const { return position; }
SizeComponent getSize() const { return size; }
void setSpriteNumber(const int v) { sprite_number = v; }
GraphicComponent(Video::SpriteSheet& spritesheet, PositionComponent& position, SizeComponent& size) :
sprite_number(0),
spritesheet(spritesheet),
position(position),
size(size)
{
all.push_back(this);
}
~GraphicComponent()
{
auto i = std::find(all.begin(), all.end(), this);
if (i != all.end())
{
all.erase(i);
}
}
};
GraphicComponent::GraphicComponentContainer GraphicComponent::all;
}
So now just use composition to do all sorts of GameObjects.
The Tile GameObjectAs I said, use composition to do our GameObject.
class Tile
{
private:
GameObject::PositionComponent position;
GameObject::SizeComponent size;
GameObject::GraphicComponent graphic;
public:
Tile(Video::SpriteSheet& spritesheet, GameObject::PositionComponent position, GameObject::SizeComponent size) :
position(position),
size(size),
graphic(GameObject::GraphicComponent(spritesheet, position, size))
{
}
};
The tile map itselfclass TileSet
{
private:
Video::SpriteSheet* spritesheet;
unsigned int first_gid;
public:
Video::SpriteSheet& getSpriteSheet() const { return *spritesheet; }
unsigned int getFirstGid() const { return first_gid; }
TileSet(SDL_Renderer* renderer, Tmx::Tileset* tileset, const char* map_path)
std::string path = map_path + tileset->GetImage()->GetSource();
SDL_Surface* sdlsurf = IMG_Load(path.c_str());
if (sdlsurf == nullptr)
{
throw(std::exception(IMG_GetError()));
}
spritesheet = new Video::SpriteSheet(renderer, sdlsurf, tileset->GetTileWidth(), tileset->GetTileHeight());
SDL_FreeSurface(sdlsurf);
}
~TileSet()
{
delete spritesheet;
}
};
class TileMap
{
public:
typedef std::vector<TileSet*> TileSetsContainer;
private:
TileSetsContainer tilesets;
std::vector<Tile*> created_tiles;
int tw;
int th;
public:
int getTileWidth() const { return tw; }
int getTileHeight() const { return th; }
TileMap(SDL_Renderer* renderer, const char* path)
{
Tmx::Map map;
map.ParseFile(path);
tw = map.GetTileWidth();
th = map.GetTileHeight();
auto tmx_tilesets = map.GetTilesets();
for (unsigned int i = 0; i < tmx_tilesets.size(); ++i)
{
tilesets.push_back(new TileSet(renderer, tmx_tilesets[i], map.GetFilepath().c_str()));
}
for (int layer = 0; layer < map.GetNumLayers(); ++layer)
{
for (int y = 0; y < map.GetLayer(layer)->GetHeight(); ++y)
{
for (int x = 0; x < map.GetLayer(layer)->GetWidth(); ++x)
{
Tmx::MapTile tile = map.GetLayer(layer)->GetTile(x, y);
if (tile.tilesetId < tilesets.size() && tile.id >= tilesets[tile.tilesetId]->getFirstGid())
{
GameObject::PositionComponent position(map.GetTileWidth() * x, map.GetTileHeight() * y);
GameObject::SizeComponent size(map.GetTileWidth(), map.GetTileHeight());
Tile* created_tile = new Tile(tilesets[tile.tilesetId]->getSpriteSheet(), position, size);
created_tiles.push_back(created_tile);
}
}
}
}
}
~TileMap()
{
for (auto i : created_tiles)
{
delete i;
}
created_tiles.clear();
for (auto i : tilesets)
{
delete i;
}
tilesets.clear();
}
};