• Hey! Guest! The 37th GMC Jam will take place between May 28th, 12:00 UTC and June 1st, 12:00 UTC. Why not join in! Click here to find out more!

Legacy GM AT-47 (Auto-tiling with 47 images)

Simon Gust

Member
GM Version: GM:S
Target Platform: All
Download: N/A
Links: N/A

Summary:
I made a script that allows you to auto-tile with 47 images but it only works with "tiles" or more precisely a grid system using an array.

Tutorial:
The algorithm is use in this script is similar to the bitwise tiling system except it doesn't count or anything.
around and noticed that say 8 calculations (1 for every direction) takes longer than, on average, 6 if-statements.
so let's begin:

first we must understand how the bitwise calcs work:
upload_2017-1-28_22-9-2.png
the brown colour represents dirt and the green colour grass.
when we check our tile or in this image "0". we can have 8 other tiles surrounding it.
If there is a tile below, add 1 to the total count etc.

at the end our sum can go from 0 to 255, which is more than 46 (47 but indexes start at 0).
In the bitwise autotiler, the numbers that are faulty are just changed manually.
Here though we don't have to do that.
Code:

Code:
///scr_get_index(array, tile, x, y)
var array = argument0; // the array to check on
var tile = argument1; // The tile type to check for
var xx = argument2; // x of the tile
var yy = argument3; // y of the tile

// directional shortcuts
var lft = xx - 1;
var top = yy - 1;
var rgt = xx + 1;
var bot = yy + 1;

//1+
if (array[@ xx, bot] != tile)
{
    //1-2+
    if (array[@ rgt, yy] != tile)
    {
        //1-2-4+
        if (array[@ xx, top] != tile)
        {
            //1-2-4-8
            if (array[@ lft, yy] != tile)
            {
                return (15);
            }
            else return (7);
        }
        else
        {
            //1-2-8
            if (array[@ lft, yy] != tile)
            {
                return (11);
            }
            else
            {
                //1-2-64
                if (array[@ lft, top] != tile)
                {
                    return (27);
                }
                else  return (3);
            }
        }
    }
    else
    {
        //1-4+
        if (array[@ xx, top] != tile)
        {
            //1-4-8
            if (array[@ lft, yy] != tile)
            {
                return (13);
            }
            else return (5);
        }
        else
        {
            //1-8+
            if (array[@ lft, yy] != tile)
            {
                //1-8-32
                if (array[@ rgt, top] != tile)
                {
                    return (23);
                }
                else return (9);
            }
            else
            {
                //1-32
                if (array[@ rgt, top] != tile)
                {
                    //1-32-64
                    if (array[@ lft, top] != tile)
                    {
                        return (43);
                    }   
                    else return (21);
                }
                else
                {
                    //1-64
                    if (array[@ lft, top] != tile)
                    {
                        return (25);
                    }
                    else return (1);
                }
            } 
        }
    }
}
else
{
    //2+
    if (array[@ rgt, yy] != tile)
    {
        //2-4
        if (array[@ xx, top] != tile)
        {
            //2-4-8
            if (array[@ lft, yy] != tile)
            {
                return (14);
            }
            else
            {
                //2-4-128
                if (array[@ lft, bot] != tile)
                {
                    return (31);
                }
                else return (6);
            }
        }
        else
        {
            //2-8
            if (array[@ lft, yy] != tile)
            {
                return (10);
            }
            else
            {
                //2-64
                if (array[@ lft, top] != tile)
                {
                    //2-64-128
                    if (array[@ lft, bot] != tile)
                    {
                        return (44);
                    }
                    else return (26);
                }
                else
                {
                    //2-128
                    if (array[@ lft, bot] != tile)
                    {
                        return (29);
                    }
                    else return (2);
                }
            }
        }
    }
    else
    {
        //4+
        if (array[@ xx, top] != tile)
        {
            //4-8
            if (array[@ lft, yy] != tile)
            {
                //4-8-16
                if (array[@ rgt, bot] != tile)
                {
                    return (18);
                }
                else return (12);
            }
            else
            {
                //4-16
                if (array[@ rgt, bot] != tile)
                {
                    //4-16-128
                    if (array[@ lft, bot] != tile)
                    {
                        return (37);
                    }
                    else return (17);
                }
                else
                {
                    //4-128
                    if (array[@ lft, bot] != tile)
                    {
                        return (30);
                    }
                    else return (4);
                }
            }
        }
        else
        {
            //8+
            if (array[@ lft, yy] != tile)
            {
                //8-16
                if (array[@ rgt, bot] != tile)
                {
                    //8-16-32
                    if (array[@ rgt, top] != tile)
                    {
                        return (33);
                    }
                    else return (19);
                }
                else
                {
                    //8-32)
                    if (array[@ rgt, top] != tile)
                    {
                        return (22);
                    }
                    else return (8);
                }
            }
            //ONLY CORNERS
            else
            {
                //16+
                if (array[@ rgt, bot] != tile)
                {
                    //16-32
                    if (array[@ rgt, top] != tile)
                    {
                        //16-32-64
                        if (array[@ lft, top] != tile)
                        {
                            //16-32-64-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (39);
                            }
                            else return (35);
                        }
                        else
                        {
                            //16-32-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (42);
                            }
                            else return (32);
                        }
                    }
                    else
                    {
                        //16-64
                        if (array[@ lft, top] != tile)
                        {                         
                            //16-64-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (41);
                            }
                            else return (34);                         
                        }
                        else
                        {
                            //16-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (36);
                            }
                            else return (16);
                        }                                   
                    }
                }
                else
                {
                    //32+
                    if (array[@ rgt, top] != tile)
                    {
                        //32-64
                        if (array[@ lft, top] != tile)
                        {
                            //32-64-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (40);
                            }
                            else return (46);
                        }
                        else
                        {
                            //32-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (38);
                            }
                            else return (20);
                        }
                    }
                    else
                    {
                        //64+
                        if (array[@ lft, top] != tile)
                        {
                            //64-128
                            if (array[@ lft, bot] != tile)
                            {
                                return (45);
                            }
                            else return (24);
                        }
                        else
                        {
                            //128+
                            if (array[@ lft, bot] != tile)
                            {
                                return (28);
                            }
                            else return (0);
                        }
                    }
                }
            }
        }
    }
}
what we do is checking if the tile on whatever direction is NOT our own.
Using != instead of == is faster in this situation.

How it works:
first it checks below, if that tile is not one of ours then go ahead, next tile
right? not our tile, go on
above? yes this a tile we possess, let's else this up.
now we are at 1-2-8
we can see that 16 and 32 are no options anymore which saves time on average.

and so on...

Other stuff:
global.tiles represents my array which is filled with all tiles and potential collision.

On the side where we call this script from is just an iteration of our array with double for loops:
Code:
//FUNCTIONS START
var array = global.tiles;
for (var i = 1; i < width-1; ++i)
{
    for (var j = 1; j < height-1; ++j)
    {
        var tile = array[@ i, j];
        var index = scr_get_index(array, tile,i,j);
  
        //DRAW THE TILE
  
    }
}
And here just the tileset for this to work properly:
upload_2017-1-28_22-26-15.png

!You may use it as reference!

If you have optimizations, complaints or questions post them here =]

Edit: Did some tests with comparing windows to yyc and this algorythm against the bitwise one
//>
x47 win (my) 1370ms 1380ms 1387ms
x47 win (bit) 2125ms 2136ms 2121ms

x47 yyc (my) 502ms 494ms 485ms
x47 yyc (bit) 657ms 676ms 686ms
//>

//>
x16 win (my) 1220ms 1216ms 1222ms
x16 win (bit) 1473ms 1462ms 1464ms

x16 yyc (my) 423ms 426ms 425ms
x16 yyc (bit) 439ms 430ms 442ms
//>
 
Last edited:
D

Deadlyapples

Guest
GMS 2 has an auto tile feature but it only works while you create the level not in level generation that is procedural or randomly generated or generated at run time. This method could be used when ever. I imagine you can use it even when you are making a mining game that upon removing a block or changing the ds_grid, re runs the above script and rebuilds the tiles to look correct. :D
 

mazimadu

Member
I was forced to by GMS 2 after yoyo pulled it from the store. Sorry, but your thing would have been useful if I could just download 1.4:(
 
K

Kenny

Guest
GM Version: GM:S
Target Platform: All

Sorry for asking this really stupid question, but can you please explain how to use this? I tried creating an object, and the code into the Create and step event, but I don't see where it references your 47-tileset background. I was excited to see this for GMS:1. Any help is appreciated, and thank you very much for posting this!
 

Simon Gust

Member
This isn't for objects at all. This is for entries in a 2D array.
Though it can be translated with not too much difficulties to objects.
As a refrence I used the image in this tutorial.
upload_2017-6-12_18-4-28.png

You have to arrange your tileset like this. Also note that it is a sprite, not a background / tileset actually.
You get 47 sub-images with this one.
 
K

Kenny

Guest
Man, as soon as I think I start to get it, I stop getting it...
I get that I need a background image, set up to be a 16x16 tile set (in the same order as yours) (awesome art skills BTW).

I think I get now that the main script is cycling through a 2D array, to identify the values at [@ x, y].

I don't quite understand how the 2d array is being created. Did you have to manually key in all the values (e.g. global.tiles[0,0] = 0) based on a manual calculation of the bitwise "calculator" or did you use a for-loop? I thought I saw a patter, then my brain broke...

I am a little confused on what to put under "//Draw the tile". I was thinking tile_add(bg_name, etc.), but I don't see enough data to fill in all the arguments.

I checked out a couple other sites to get more information, but I feel like I'm not getting everything (here was a good site: https://gamedevelopment.tutsplus.com/tutorials/how-to-use-tile-bitmasking-to-auto-tile-your-level-layouts--cms-25673) Also, everyone uses bitwise calculations in a different order.

If you happen to make a Youtube video on this, you will get a like/subscribe right away, lol!
 
K

Kenny

Guest
OK, I'm a spaz. I get it now that you have a sprite, with 47 images. I am still not getting what data is in the array though. So much to learn, so much time to learn it in, so little brain power to process it all!
 

Simon Gust

Member
Yeah you're almost got it. The array is basically a bunch of terrain info.
The basics is:
0 = nothing,
1 = dirt
2 = stone
3 = grass
for example.
I have some kind of generator that fills the information in some algorythm.

Though I have to say, this autotiler is old and I don't use it anymore, I've switched to shaders in the meantime, but it's still fast.
 
Top