Hostinicon
GET HOSTING FROM $3.95/MO PLUS A FREE YEAR ON TUTS+ (RRP $180). HURRY OFFER LIMITED. Check it out
Advertisement

Parsing and Rendering Tiled TMX Format Maps in Your Own Game Engine

by
Gift

Get a free year on Tuts+ this month when you purchase a Siteground hosting plan from $3.95/mo

In my previous article, we looked at Tiled Map Editor as a tool for making levels for your games. In this tutorial, I'll take you through the next step: parsing and rendering those maps in your engine.

Note: Although this tutorial is written using Flash and AS3, you should be able to use the same techniques and concepts in almost any game development environment.


Requirements


Saving in XML Format

Using the TMX specification we can store the data in a variety of ways. For this tutorial we will be saving our map in the XML format. If you plan on using the TMX file included in the requirements section you can skip to the next section.

If you made your own map you will need to tell Tiled to save it as XML. To do this, open your map with Tiled, and select Edit > Preferences...

For the "Store tile layer data as:" dropdown box, select XML, as shown in the image below:

Now when you save the map it will be stored in XML format. Feel free to open the TMX file with a text editor to take a peek inside. Here's a snippet of what you can expect to find:

As you can see, it simply stores all the map information in this handy XML format. The properties should mostly be straightforward, with the exception of gid - I will go into a more in-depth explanation of this later on in the tutorial.

Before we move on, I would like to direct your attention to the objectgroup "Collision" element. As you may recall from the map creation tutorial, we specified the collision area around the tree; this is how it is stored.

You can specify power-ups or player spawn point in the same manner, so you can imagine how many possibilities there are for Tiled as a map editor!


Core Outline

Now here's a brief rundown on how we will be getting our map into the game:

  1. Read in the TMX file.
  2. Parse the TMX file as an XML file.
  3. Load all the tileset images.
  4. Arrange the tileset images into our map layout, layer by layer.
  5. Read map object.

Reading in the TMX File

As far as your program is concerned, this is just an XML file, so the first thing we want to do is read it in. Most languages have an XML library for this; in the case of AS3 I will use the XML class to store the XML information and a URLLoader to read in the TMX file.

This is a simple file reader for "../assets/example.tmx". It assumes that the TMX file is located in your project directory under the "assets" folder. We just need a function to handle when the file read is complete:

This is where the initial parsing is taking place. (There are a few variables we will keep track of outside this function since we will use them later.)

Once we have the map data stored, we move onto parsing each tileset. I've created a class to store each tileset's information. We'll push each of those class instances in an array since we will be using them later:

Again, you can see that gid appears again, in the firstgid and lastgid variables. Let's now look at what this is for.


Understanding "gid"

For each tile, we need to somehow associate it with a tileset and a particular location on that tileset. This is the purpose of the gid.

Look at the grass-tiles-2-small.png tileset. It contains 72 distinct tiles:

We give each of these tiles a unique gid from 1-72, so that we can refer to any one with a single number. However, the TMX format only specifies the first gid of the tileset, since all of the other gids can be derived from knowing the size of the tileset and the size of each individual tile.

Here's a handy image to help visualize and explain the process.

So if we placed the bottom right tile of this tileset on a map somewhere, we would store the gid 72 at that location on the map.

Now, in the example TMX file above, you will notice that tree2-final.png has a firstgid of 73. That's because we continue counting up on the gids, and we don't reset it to 1 for each tileset.

In summary, a gid is a unique ID given to each tile of each tileset within a TMX file, based on the position of the tile within the tileset, and the number of tilesets referred to in the TMX file.


Loading the Tilesets

Now we want to load all the tileset source images into memory so we can put our map together with them. If you aren't writing this in AS3, the only thing you need to know is that we're loading the images for each tileset here:

There's a few AS3-specific things going on here, such as using the Loader class to bring in the tileset images. (More specifically, it is an extended Loader, simply so we can store the TileSet instances inside each Loader. This is so that when the loader completes we can easily correlate the Loader with the tileset.)

This may sound complicated but the code is really quite simple:

Now before we start taking these tilesets and creating the map with them we need to create a base image to put them on:

We will be copying the tile data onto these bitmap images so that we can use them as a background. The reason I set up two images is so we can have a top layer and a bottom layer, and have the player move in-between them in order to provide perspective. We also specify that the top layer should have an alpha channel.

For the actual event listeners for the loaders we can use this code:

This is a fun function since you can track how far the image has loaded, and can therefore provide feedback to the user about how fast things are going, such as a progress bar.

Here we store the bitmap data with the tileset associated with it. We also count how many tilesets have completely loaded, and when they're all done, we can call a function (I named it addTileBitmapData in this case) to start putting the tile pieces together.


Combining the Tiles

To combine the tiles into a single image, we want to build it up layer by layer so it will be displayed the same way the preview window in Tiled appears.

Here is what the final function will look like; the comments I've included within the source code should adequately explain what's going on without getting too messy into the details. I should note that this can be implemented in many different ways, and your implementation can look completely different than mine.

What's happening here is we're parsing only the tiles with gids that are above 0, since 0 indicates an empty tile, and storing them in an array. Since there are so many "0 tiles" in our top layer, it would be inefficient to store all of them in memory. It's important to note that we're storing the location of the gid with a counter because we will be using its index in the array later.

In this section we're parsing out the layer name, and checking if it's equal to "Top". If it is, we set a flag so we know to copy it onto the top bitmap layer. We can be really flexible with functions like this, and use even more layers arranged in any order.

Now here we're storing the gid, which we parsed at the beginning, into a 2D array. You'll notice the double array initializations; this is simply a way of handling 2D arrays in AS3.

There's a bit of math going on as well. Remember when we initialized the tiles array from above, and how we kept the index with it? We will now use the index to calculate the coordinate that the gid belongs to. This image demonstrate what's going on:

So for this example, we get the gid at index 27 in the tiles array, and store it at tileCoordinates[7][1]. Perfect!

This is where we finally get down to copying the tileset into our map.

Initially we start by looping through each tile coordinate on the map, and for each tile coordinate we get the gid and check for the stored tileset that matches it, by checking if it lies between the firstgid and our calculated lastgid.

If you understood the Understanding "gid" section from above, this math should make sense. In the most basic terms, it's taking the tile coordinate on the tileset (sourceX and sourceY) and copying it onto our map at the tile location we've looped to (destX and destY).

Finally, at the end we call the copyPixel function to copy the tile image onto either the top or base layer.


Adding Objects

Now that copying the layers onto the map is done, let's look into loading the collision objects. This is very powerful because as well as using it for collision objects, we can also use it for any other object, such as a power-up, or a player spawn location, just as long as we've specified it with Tiled.

So at the bottom of the addTileBitmapData function, let's put in the following code:

This will loop through the object layers, and look for the layer with the name "Collision". When it finds it, it takes each object in that layer, creates a rectangle at that position and stores it in the collisionTiles array. That way we still have a reference to it, and we can loop through to check it for collisions if we had a player.

(Depending on how your system handles collisions, you may want to do something different.)


Displaying the Map

Finally, to display the map, we want to first render the background and then the foreground, in order to get the layering correct. In other languages, this is simply a matter of rendering the image.

I've added a bit of code in between the layers here just to demonstrate with a rectangle that the layering does indeed work. Here's the final result:

Thank you for taking the time to complete the tutorial. I've included a zip containing a complete FlashDevelop project with all of the source code and assets.


Additional Reading

If you're interested in doing more things with Tiled, one thing I didn't cover was properties. Using properties is a small jump from parsing the layer names, and it allows you to set a large number of options. For example, if you wanted an enemy spawn point, you could specify the type of enemy, the size, the colour, and everything, from inside the Tiled map editor!

Lastly, as you may have noticed, XML is not the most efficient format to store the TMX data. CSV is a nice medium between easy parsing and better storage, but there is also base64 (uncompressed, zlib compressed, and gzip compressed). If you're interested in using these formats instead of XML, check out the Tiled wiki's page on the TMX format.

Advertisement