#include <SFML/Graphics.hpp>
#include "pugixml/pugixml.hpp"
#include <iostream>
class TileMap final : public sf::Drawable, public sf::Transformable
{
public:
TileMap()
: mVertices()
, mTileset()
{
}
bool loadFromFile(const std::string& filename)
{
pugi::xml_document mapDoc;
if (!mapDoc.load_file(filename.c_str()))
{
std::cerr << "Loading level \"" + filename + "\" failed.\n";
return false;
}
auto mapNode = mapDoc.child("map");
auto width = mapNode.attribute("width").as_uint();
auto height = mapNode.attribute("height").as_uint();
auto tileWidth = mapNode.attribute("tilewidth").as_uint();
auto tileHeight = mapNode.attribute("tileheight").as_uint();
mVertices.setPrimitiveType(sf::Quads);
mVertices.resize(width * height * 4);
auto tilesetNode = mapNode.child("tileset");
auto firstTileID = tilesetNode.attribute("firstgid").as_uint();
auto image = tilesetNode.child("image");
std::string imagePath = image.attribute("source").as_string();
auto x = imagePath.find_first_of("./");
auto split = imagePath.substr(++x, imagePath.size());
if (!mTileset.loadFromFile("Media" + split))
{
std::cerr << "can't laod texture: Media" + split + "\n";
return false;
}
auto layerNode = mapNode.child("layer");
while (layerNode)
{
auto dataNode = layerNode.child("data");
auto tileNode = dataNode.child("tile");
for (auto j = 0u; j < height; ++j)
{
for (auto i = 0u; i < width; ++i)
{
auto tileGID = tileNode.attribute("gid").as_uint();
tileGID -= firstTileID;
auto tu = tileGID % (mTileset.getSize().x / tileWidth);
auto tv = tileGID / (mTileset.getSize().x / tileWidth);
auto* quad = &mVertices[(i + j * width) * 4];
quad[0].position = { static_cast<float>(i * tileWidth), static_cast<float>(j * tileHeight) };
quad[1].position = { static_cast<float>((i + 1) * tileWidth), static_cast<float>(j * tileHeight) };
quad[2].position = { static_cast<float>((i + 1) * tileWidth), static_cast<float>((j + 1) * tileHeight) };
quad[3].position = { static_cast<float>(i * tileWidth), static_cast<float>((j + 1) * tileHeight) };
quad[0].texCoords = { static_cast<float>(tu * tileWidth), static_cast<float>(tv * tileHeight) };
quad[1].texCoords = { static_cast<float>((tu + 1) * tileWidth), static_cast<float>(tv * tileHeight) };
quad[2].texCoords = { static_cast<float>((tu + 1) * tileWidth), static_cast<float>((tv + 1) * tileHeight) };
quad[3].texCoords = { static_cast<float>(tu * tileWidth), static_cast<float>((tv + 1) * tileHeight) };
tileNode = tileNode.next_sibling("tile");
}
}
layerNode = layerNode.next_sibling("layer");
}
return true;
}
sf::FloatRect getLocalBounds() const
{
return mVertices.getBounds();
}
sf::FloatRect getGlobalBounds() const
{
return getTransform().transformRect(mVertices.getBounds());
}
private:
void draw(sf::RenderTarget& target, sf::RenderStates states) const override
{
states.transform *= getTransform();
states.texture = &mTileset;
target.draw(mVertices, states);
}
private:
sf::VertexArray mVertices;
sf::Texture mTileset;
};
int main()
{
sf::RenderWindow window(sf::VideoMode(1024, 512), "Tilemap");
// create a view half the size of the default view
sf::View view = window.getDefaultView();
view.zoom(0.5f);
view.setCenter(view.getSize().x / 1.25f, view.getSize().y);
TileMap map;
if (!map.loadFromFile("Media/Maps/test002.tmx"))
return -1;
map.setPosition(window.getView().getSize() / 2.f);
auto bounds(map.getLocalBounds());
map.setOrigin(std::floor(bounds.left + bounds.width / 2.f), std::floor(bounds.top + bounds.height / 2.f));
sf::Clock clock;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
sf::Time dt = clock.restart();
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
view.move(100.f * dt.asSeconds(), 0.f);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
view.move(-100.f * dt.asSeconds(), 0.f);
}
window.setView(view);
window.clear();
window.draw(map);
window.display();
}
}