• Hey! Guest! The 40th (!!!) GMC Jam will take place between February 25th, 12:00 UTC to March 1st 12:00 UTC. Why not join in this very special anniversary jam! Click here to find out more!

Using Tiled Maps

Mystborn

Member
GM Version: GM:S
Target Platform: All (Probably)
Download: Dropbox
Links: Tiled

Summary
This tutorial teaches how to use a script in order to use tiled maps in gamemaker. It should be noted that this will exclusively spawn tiles, not objects.

Tutorial

What is Tiled

Tiled is a free map editor for use specifically with tilesets. By default it saves the maps as an XML document, with a .tmx file extension.

This script searches through the xml tags for specific keywords in order to generate the tiles.

The usage of this script is relatively simple, but the backgrounds in gamemaker have to have the same name as the image name in the .tmx file. You can just edit the file in a text editor and change it in there, or you can follow the instructions outlined in the script. As long as you have the tiled map saved in the appdata location of your project it should work. If you were to use it on a release build you'd put the map in your included files.

It is missing some functionality. Once the script is finished, there is no way to access the variables inside the .tmx file. In addition to that, it doesn't handle any custom properties. It is simply used to easily spawn the tiles.

To use the script, download it from the dropbox link, right click on the scipts folder, and click add existing script. or you can go ahead and just copy it from below if you choose.

Code:
///scr_import_tiled(fname, starting_depth)
//IMPORTANT
//The source tilesets and the gamemaker background asset must have the same name
//Look under the image tag to find a way to fix if you wish.



var fname = argument[0];
var starting_depth = argument[1];
var height, tilewidth, tileheight, setwidth, setheight;
var firstgid, columns, background, sets, layer, opacity;

sets = 0;
layer = 0;

//If the file doesn't exist, exit early
if(!file_exists(fname)) {
    return false;
}
var file = file_text_open_read(fname);

/* Loop through all lines of the file, looking for specific tags.
   When we find a wanted tag, we'll look for the variable names wanted
   and save them for actually spawning the tiles.
*/
while(!file_text_eof(file)) {
    line = file_text_read_string(file);
    if(string_pos("<map ", line) != 0) {
        //From the map tag, look for tilewidth and tileheight.
        //These are used for the spacing between tiles
        //Also need to get the height to check for the last row later
 
        var index = string_pos("tilewidth", line);
        index += 11;
        tilewidth = "";
        while(string_char_at(line, index) != '"') {
            tilewidth += string_char_at(line, index++);
        }
        tilewidth = real(tilewidth);
   
        index = string_pos("tileheight", line);
        index += 12;
        tileheight = "";
        while(string_char_at(line, index) != '"') {
            tileheight += string_char_at(line, index++);
        }
        tileheight = real(tileheight);
   
        index = string_pos("height", line);
        index += 8;
        height = "";
        while(string_char_at(line, index) != '"') {
            height += string_char_at(line, index++);
        }
        height = real(height);
   
    } else if (string_pos("<tileset ", line) != 0) {
        //There can be multiple tileset tags, so the values are
        //saved in an array. Looking for the firstgid which is used
        //to determine the right background, the tilewidth and tileheight
        //for individual tiles, and the columns, which is used to determine
        //the tiles position in the BACKGROUND
 
        var index = string_pos("firstgid", line);
        index += 10;
        firstgid[sets] = "";
        while(string_char_at(line, index) != '"') {
            firstgid[sets] += string_char_at(line, index++);
        }
        firstgid[sets] = real(firstgid[sets]);
   
        index = string_pos("tilewidth", line);
        index += 11;
        setwidth[sets] = "";
        while(string_char_at(line, index) != '"') {
            setwidth[sets] += string_char_at(line, index++);
        }
        setwidth[sets] = real(setwidth[sets]);
   
        index = string_pos("tileheight", line);
        index += 12;
        setheight[sets] = "";
        while(string_char_at(line, index) != '"') {
            setheight[sets] += string_char_at(line, index++);
        }
        setheight[sets] = real(setheight[sets]);
   
        index = string_pos("columns", line);
        index += 9;
        columns[sets] = "";
        while(string_char_at(line, index) != '"') {
            columns[sets] += string_char_at(line, index++);
        }
        columns[sets] = real(columns[sets]);
   
        sets++;
    } else if (string_pos("<image ", line) != 0) {
        //From the image tag, look for the background name, then get the
        //asset index. Mentioned at the top, if your background name in
        //gamemaker is different from the filename, change the filename
        //to reflect this. Or if you wanted before the last line you could
        // put background[sets - 1] = "bg_" + background[sets - 1]
   
        //sets needs to be subtracted by one because this will come after the
        //tileset tag where it's incremented. Should move the increment to here
        //but I'm too lazy tbh.
 
        var index = string_pos("source", line);
        index += 8;
        var source = "";
        while(string_char_at(line, index) != '"') {
            source += string_char_at(line, index++);
        }
   
        index = 0;
        var temp = noone;
        while(temp != 0) {
            temp = string_pos("/", source);
            source = string_delete(source, temp, 1);
            if(temp != 0) {
                index = temp;
            }
        }
   
        background[sets - 1] = "";
        while(string_char_at(source, index) != '.') {
            background[sets - 1] += string_char_at(source, index++);
        }
        background[sets - 1] = asset_get_index(background[sets - 1]);
    } else if (string_pos("<layer ", line) != 0) {
        var index = string_pos("opacity", line);
        if(index == 0) {
            opacity = 1.00;
        }
        else {
            index += 9;
            var alpha = "";
            while(string_char_at(line, index) != '"') {
                alpha += string_char_at(line, index++);
            }
            alpha = real(alpha);
            opacity = alpha;
        }
    }
    else if (string_pos("<data ", line) != 0) {
        //This is where the magic happens. Step by step explanation
 
        //We need to skip to the next line where the variables start
        file_text_readln(file);
   
        var ey = 0;
   
        //Loop through lines until we find a closing tag
        while(string_pos("</data>", line) == 0) {
   
            line = file_text_readln(file);
       
            //This variable keeps track of the last comma
            var last = 1;
            var ex = 0;
       
            //if we're on the last line we need to add an extra comma to the end
       
            if(ey == height - 1) {
                line += ",";
            }
       
            //Loop through each char in the line, pausing when we hit a comma
            for(var i = 1; i <= string_length(line); i++) {
                if(string_char_at(line, i) == ",") {
           
                    //Retrieve the gid starting from right after the last comma
                    //through the current comma
                    var gid = real(string_copy(line, last, i - last));
                    tile_id = gid;
                    last = i + 1;
               
                    //If there's no tile value, increment the x pos and continue
                    if(gid == 0) {
                        ex++;
                        continue;
                    }
               
                    var bg = 0;
                    for(var l = 0; l < sets; l++) {
                        //As long as the gid is bigger or equal to the firstgid
                        //of a tilest, it's at least a part of that tileset.
                        if(gid >= firstgid[l]) {
                            bg = l;
                       
                            //Do this to find the tile number in the tileset
                            tile_id -= firstgid[l];
                        }
                    }
               
                    //The above will only find what number the tile is in the
                    //tileset. we need to find the position of the tile.
                    bg_x = tile_id % columns[bg];
                    bg_y = (tile_id - bg_x) / columns[bg];
               
                    //Add the tile to the screen!
                    //The starting depth is subtracted by the layer to have them appear
                    //proper order
                    var t = tile_add(background[bg], bg_x * setwidth[bg], bg_y * setheight[bg], setwidth[bg],
                    setheight[bg], ex * tilewidth, ey * tileheight, starting_depth - layer);
                    tile_set_alpha(t, opacity);
                    ex++;
                }
            }
            ey++;
        }
        layer++;
    }
 
    file_text_readln(file);
}



file_text_close(file);

return true;
edit: Added blurb about Tiled as per chance's suggestion.
 
Last edited:

chance

predictably random
Forum Staff
Moderator
This is OK for Tutorials. But it would be helpful if you made it clear what a .tmx file is. Maybe provide some links to the Tiled editor, and say something about the file format and specifications.

That way, members will understand that your script is written to work with the specific type of map file produced by that editor -- and not just tile maps in general.
 

bbbower

Member
You missed out on getting credit where credit is due here. This is a perfect solution for loading levels at runtime. I had been looking for something like this for a while. I could probably write the code myself but was so stuck on finding a way to just convert the .tmx into gms room files (because that's all that comes up in searches) that I didn't entertain the idea of just reading the data during runtime from the original files. WHICH IS SO MUCH BETTER! Now I can make my changes to the Tiled maps and they show up in my compiled game without the need to re-compile. I don't think folks have grasped the usefulness of this yet. If I make any improvements on this I'll post it here, and I hope you haven't abandoned working on this script. If you have let me know and I'll probably take up the charge myself.

[edit] upon trying the script i'm getting a bit of errors (probably due to changes in tmx format and saving) I changed a few components around to fix it but was their a certain format you were saving your tmx files in before loading them?

[edit 2] Upon trying to revise it, it seems it only works with a much much older version of tiled, levels must be saved with CSV selected instead of XML as I would not have thought ... unfortunately much of the reading of the file seems to be changed, image is obsolete<--- I take that back, depending on how your set the tiledefs in the program it may still show.. the grids are assigned to locked values on layers so basically only the first section for reading the tile widths and heights still works the rest will need re-written or adapted depending on the way the file is saved. I wish I had found this a long time ago still however XD I'm going to pursue writing some addition to make it work with the new version changes. Still good stuff good stuff, and much thanks!

[edit 3] if anyone uses this and has the problem with the tiledef's / image not reading simply add
Code:
if argument_count > 2 background[0] = argument[2];
and add the sprite name to the end of the function when you call it. Fixes it if you are using one image for tilesets and the <image> tag is not being generated.
 
Last edited:
Top