Thursday, 30 May 2013

Tiled Map Loader for SFML

DOWNLOAD LATEST  from GitHub
Discuss / report bugs here (SFML forum thread)

UPDATE: due to some foolish variable naming on my part the map loader was unable to compile properly with gcc. I've fixed this (and tested it to work with MinGW) and updated the download below.

UPDATE  2: I've now added quadtree optimisation. Read about it here.

UPDATE 3: Finally got round to updating the rendering with a vertex array. This offers, in some cases, up to a 1000(!) frames per second increase in performance (depending on the map and the hardware it runs on), so worth re-downloading if you haven't already.

UPDATE 4:  MapObjects now update their associated data such as debug shapes and AABB when moving. I've also added overloads for the draw function to allow drawing of specific layers such as debug information, or by layer index. The minimum requirements for a C++11 compatible compiler are updated too, which means that VS10 will no longer work and will need to be upgraded to VS11. If you use a different compiler this shouldn't be a problem.

UPDATE 5: Small optimisation where image files are cached to prevent loading the same images from disc more than once (including when loading a new map).

UPDATE 6: read about it here.

UPDATE 7: information is here.

UPDATE 8: Small code refactor, and added some utility functions for returning normal vectors in collision testing. If you're upgrading from a previous version you need to add MapObject.cpp to your project.

Original post:
(note the interface described below is slightly out of date, refer to the included readme for up to date information)

Last year when experimenting with with platform physics in SFML I decided it might be fun to try creating a tile map editor. The enthusiasm was short lived, however, as it turns out that it would not only be a huge project, but that other people have already done similar things, most likely in a much better way than I would have done too... Still, not to be perturbed, I pressed on and, after scouring the internet, decided that my favourite editor was Tiled. It's easy and intuitive to use, as well as having a simple to understand xml format for map files. It supports traditional orthogonal maps (suitable for my platform experiment) as well as isometric (actually axonometric) projection for pseudo 2.5D maps. With this information in hand the next thing to do was to check if anyone had already written a tmx (Tiled's map file format) parser for SFML. I soon found this thread on the SFML forums which provides some basic code. Unfortunately it is written for the older 1.6 version of SFML and only supports some of Tiled's basic features. If you follow the thread you'll see that I (posting under the moniker fallahn) updated the supplied code to use SFML 2.0 as well as adding features like support for compressed and encoded map formats. This sufficed for my own needs, and credit must go to the original author of the code for putting me on the right track, but, more recently (particularly with a newer release of Tiled which has added more features), I decided it was time for a complete rewrite, creating a new SFML compatible class from scratch. This, then, leads me to what this post is really about. I wanted to make the feature set as complete as possible so I made sure to consider everything I could so that it would be properly included in the code rather than being hacked in at a later date. As a result I now have a class which looks like this:

MapLoader::MapLoader(std::string mapDirectory);
const bool MapLoader::Load(std::string mapFile);
std::vector<MapLayer>& GetLayers(void);
void MapLoader::Draw(sf::RenderTarget& rt);

const sf::Vector2f OrthogonalToIsometric(const sf::Vector2f& worldCoords);
const sf::Vector2f IsometricToOrthogonal(const sf::Vector2f& projectedCoords);

I've deliberately tried to keep the interface as minimal as possible, requiring only one call to the constructor and Load() function, and a call to Draw() each render loop to get a map loaded and drawing on screen. GetLayers() provides access to a vector of MapLayer objects which contain data such as tile sprites or MapObjects. The MapObject class stores information of a single object on an object layer, such as its position or polygonal shape. The last two functions are utilities for converting coordinates between orthogonal and isometric world spaces, and are only useful for isometric maps. The original code never supported isometric projection, and was something I was keen to add in the new class. The full list of features are:

Uses pugixml (included) to parse xml
Supports orthogonal maps
Supports isometric maps
Supports conversion between orthogonal and isometric world coords
Parses all types of layers (normal, object and image), layer properties
Parses all type of object, object shapes, types, properties
Option to draw debug output of objects, grid and object names
Supports multiple tile sets, including tsx files
Supports all layer encoding and compression: base64, csv, zlib, gzip and xml (requires zlib library, see /lib directory)


I opted to use the pugixml library rather than TinyXML as it is more compact while maintaining a similar and easy to use interface. In this version there is now also support for maps with multiple tile sets, and tsx file format tile sets which are used by Tiled, both of which are features missing from the first version. Currently there is no support for sprite flipping or rotation, but these are not yet covered by the current official release of Tiled anyway. I've put the whole set of files together along with a readme and some samples of how to use the class in an archive which you can download here, although you will need to download and link zlib yourself, as it is needed for compressed map files. If you find any bugs please feel free to post them in the comments, and I'll see what I can do to address them.