16
\$\begingroup\$

I'm looking for information about how people implement autotiling in their tile-based games. So far I have always improvised it with a bunch of hardcoded "if ... else ..." statements, and now I decided that it is time to find some more elegant solution. I went searching on the Internet for examples of autotiling implementations and discussions on the topic, but I only came up with three articles:

(Especially the last one is comprehensive and very helpful.)

I have also looked at various implementations and documentation of libraries, that implement it, for example, flixel: http://www.flixel.org/features.html#tilemaps

Sadly, all the solutions that I could find are exactly as improvised and haphazard, as what I started with, and almost never cover all the possible cases.

I'm looking for an elegant example of autotiling implementation that I could learn from.

\$\endgroup\$

3 Answers 3

13
\$\begingroup\$

I've used A Bitwise Method For Applying Tilemaps and found it to be a very elegant solution. The article provides a concrete example and discusses how to extend the algorithm to handle multiple terrain types.

\$\endgroup\$
2
  • \$\begingroup\$ This looks like the algorithm used by the flixel "AUTO" mode of autotiling. Thank you, I'm adding it to my list. \$\endgroup\$ Jan 2, 2013 at 18:02
  • 1
    \$\begingroup\$ How to Use Tile Bitmasking to Auto-Tile Your Level Layouts is another good explanation of this technique. It additionally explains how to handle corners (especially when to ignore the corners). \$\endgroup\$
    – idbrii
    Feb 13, 2022 at 16:51
5
\$\begingroup\$

I arrived here by Googling this problem myself, read the linked articles, and produced a relatively compact solution which generates the common set of 47 tiles. It requires a 2x3 tileset for the autotiled material like this: a 2x3 autotile tileset

With a single-tile variant at the top left, inner corners at the top right, and four outer corner tiles at the bottom (you may recognize this arrangement from RPG Maker).

The trick is to break each "logical" map tile into 4 half-tiles for rendering. furthermore, a half-tile in the tileset can only be in that position in a generated tile, so a top-left half tile can only be used in a top-left position.

These restrictions mean that you only need to check 3 full-tile neighbors per half-tile, instead of all 8 neighboring tiles.

I implemented this idea quickly to test it. Here's the proof-of-concept code (TypeScript):

//const dirs = { N: 1, E: 2, S: 4, W:8, NE: 16, SE: 32, SW: 64, NW: 128 };
const edges = { A: 1+8+128, B: 1+2+16, C: 4+8+64, D: 4+2+32 };
const mapA = { 0:8, 128:8, 1:16, 8:10, 9:2, 137:18, 136:10, 129:16 };
const mapB = { 0:11, 16:11, 1:19, 2:9, 3:3, 19:17, 18:9, 17:19 };
const mapC = { 0:20, 64:20, 4:12, 8:22, 12:6, 76:14, 72:22, 68:12 };
const mapD = { 0:23, 32:23, 4:15, 2:21, 6:7, 38:13, 34:21, 36:15 };

export function GenerateAutotileMap(_map: number[][], _tile: integer): number[][]
{
    var result = [];
    for (var y=0; y < _map.length; y++) {
        const row = _map[y];
        const Y = y*2;
        // half-tiles
        result[Y] = [];
        result[Y+1] = [];
        // each row
        for (var x=0; x < row.length; x++) {
            // get the tile
            const t = row[x];
            const X = x*2;
            if (t != _tile) continue;
            // Check nearby tile materials.
            const neighbors = (North(_map, x, y) == t? 1:0)
                + (East(_map, x, y) == t? 2:0)
                + (South(_map, x, y) == t? 4:0)
                + (West(_map, x, y) == t? 8:0)
                + (NorthEast(_map, x, y) == t? 16:0)
                + (SouthEast(_map, x, y) == t? 32:0)
                + (SouthWest(_map, x, y) == t? 64:0)
                + (NorthWest(_map, x, y) == t? 128:0);
            // Isolated tile
            if (neighbors == 0) {
                result[Y][X] = 0;
                result[Y][X+1] = 1;
                result[Y+1][X] = 4;
                result[Y+1][X+1] = 5;
                continue;
            }
            // Find half-tiles.
            result[Y][X] = mapA[neighbors & edges.A];
            result[Y][X+1] = mapB[neighbors & edges.B];
            result[Y+1][X] = mapC[neighbors & edges.C];
            result[Y+1][X+1] = mapD[neighbors & edges.D];
        }
    }
    return result;
}    

Explanation:

  • A is the top left part of the tile, B is the top right, C is the bottom left, D is the bottom right.
  • edges holds bitmasks for each of these, so we can grab only the relevant neighbor info.
  • map* are dictionaries mapping neighbor states to graphic indices in the tileset image (0..24).
    • since each half-tile checks 3 neighbors, each one has 2^3=8 states.
  • _tile is the tile targeted for autotiling.
  • Since our logical tiles are twice as large as our rendering tiles, all the autotile coords (x,y) have to be doubled in the rendering map.

Anyway, here are the results (with only one tile, anyway):enter image description here

\$\endgroup\$
1
  • \$\begingroup\$ Here's a Lua implementation of this technique. Works well for overhead maps, but I haven't been able to make it look good for side scrollers. \$\endgroup\$
    – idbrii
    Feb 7, 2022 at 4:45
0
\$\begingroup\$

I read most of the links and spent some time to come up with another solution. I don't know if it's good or not, but to simulate the RPG Maker VX Ace auto-tile behavior (47 tiles) I started doing something like this:

(left 0 or 1) + (right 0 or 1) + (up 0 or 1) + (down 0 or 1) now I have 5 cases.

if 4 = Tile 46 is placed

if 3 boarders =

if 2 4 cases + 2 cases not sure about algorithm but not much branches to make.

if 1 = working on it but every direction can end up in 4 cases

if 0 = I can use the number algorithm shown in the links with 1, 2, 4, 8 and getting id from 1 to 15 I can directly use.

I am not a programmer and not the best with math algorithms and the 1, 2, 4, 8, 16, 32, 64, 128 solution I also didn't liked much.

Maybe my approach is at least better than that.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I'm not sure this answer fully answers the question, could you explain a bit more? If you refer to something else, could you at least link to it? \$\endgroup\$
    – Vaillancourt
    Nov 6, 2015 at 23:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .