1
\$\begingroup\$

So I am making a tiled game and I need to connect textures correctly based on whats next to it.

With tiles that had connection really close to the edge of the tile I could just use the 4 bit meathod ie:

x 1 x
8 x 2
x 4 x

But for tiles with more detail I need to have corners too. I have seen people use the 8 bit meathod ie:

1   2  4
128 x  8
64  32 16

But really I can only see 48 different tile combos.

Is there a way I can separate corners and u d l r. Something like this:

x 1 x  8 x 1
8 x 2  x x x
x 4 x  4 x 2

Or is there a better way all together to do this.

This is what I have tried so far but this only uses 32/48 tiles and doesn't work for special corner cases.

public class RockTile extends Tile {

int y;

public RockTile(int id) {
    super(id);
    y = 7;
}

public TextureRegion getTexture(Level level, int xLoc, int yLoc){

    int u = (level.getTile(xLoc, yLoc + 1) != this) ? 1 : 0;
    int r = (level.getTile(xLoc + 1, yLoc) != this) ? 2 : 0;
    int d = (level.getTile(xLoc, yLoc - 1) != this) ? 4 : 0;
    int l = (level.getTile(xLoc - 1, yLoc) != this) ? 8 : 0;

    int bit = u + r + d + l;

    int ur = (level.getTile(xLoc + 1, yLoc + 1) != this) ? 1 : 0;
    int dr = (level.getTile(xLoc + 1, yLoc - 1) != this) ? 2 : 0;
    int dl = (level.getTile(xLoc - 1, yLoc - 1) != this) ? 4 : 0;
    int ul = (level.getTile(xLoc - 1, yLoc + 1) != this) ? 8 : 0;
    int corner = ul + ur + dl + dr;

    if (bit == 0) {
        if (corner != 0)
            return Game.splitTiles[y + 1][corner];
        else
            return Game.splitTiles[y][bit];

    } else {
        if (corner != 0)
            if (dl > 0 && l == 0 && d == 0)
                return Game.splitTiles[y + 2][bit];
            else
                return Game.splitTiles[y][bit];

        else
            return Game.splitTiles[y + 2][bit];
    }

}

}

If you need anything else let me know.

EDIT: Side question because I've seemed to have stumped you guys. If I decided to use the 8 bit method what would be the best and fastest way to make a lookup table for that.

\$\endgroup\$
1
\$\begingroup\$

For my system I use a spritesheet with 16 tile variations on it.

Upon creating a tile type I cut up the texture into regions specified in the constructor (which means my tiles can have varying resolutions).

That means you can provide it with an image like this http://www.promagra.de/Downloads/Platformer/Pl5t.png and the transitions will take care of themselves.

The is my simplified constructor for a tile type:

public class TileType {

    public final TextureRegion[] tileVariations;

    public TileType(String name, Texture texture, int regionSize){

        tileVariations = new TextureRegion[16];

        TextureRegion[][] tmp = TextureRegion.split(texture, regionSize, regionSize);

        tileVariations[0] = tmp[3][0];
        tileVariations[1] = tmp[2][0];
        tileVariations[2] = tmp[3][1];
        tileVariations[3] = tmp[2][1];
        tileVariations[4] = tmp[0][0];
        tileVariations[5] = tmp[1][0];
        tileVariations[6] = tmp[0][1];
        tileVariations[7] = tmp[1][1];
        tileVariations[8] = tmp[3][3];
        tileVariations[9] = tmp[2][3];
        tileVariations[10] = tmp[3][2];
        tileVariations[11] = tmp[2][2];
        tileVariations[12] = tmp[0][3];
        tileVariations[13] = tmp[1][3];
        tileVariations[14] = tmp[0][2];
        tileVariations[15] = tmp[1][2];

    }

}

Then every time an individual tile type has its tile type updated, I recalculate which sprite to use for the tile and its neighbours. The sprite to use is stored as an int / byte which is used to access a specific index of the tile variations array of the tile type.

Then you can simply render the correct texture of the tile type based on the display index of an individual tile.

Here is a simplified MapTile class:

public class MapTile {

    public final int xPos;
    public final int yPos;

    private final TileMap tileMap;

    private TileType tileType;
    private byte displayTile = 0;

    public MapTile(int x, int y, TileMap map){
        xPos = x;
        yPos = y;
        tileMap = map;
    }

    public void setTileType(TileType newType){
        tileType = newType;

        // Check the current tile
        checkNeighbours();

        MapTile neighbourTile;

        // Check the tile above
        neighbourTile = tileMap.getTile(xPos, yPos + 1);
        if(neighbourTile != null){
            neighbourTile.checkNeighbours();
        }
        // Check the tile below
        neighbourTile = tileMap.getTile(xPos, yPos - 1);
        if(neighbourTile != null){
            neighbourTile.checkNeighbours();
        }
        // Check the tile to the left
        neighbourTile = tileMap.getTile(xPos - 1, yPos);
        if(neighbourTile != null){
            neighbourTile.checkNeighbours();
        }
        // Check the tile to the right
        neighbourTile = tileMap.getTile(xPos + 1, yPos);
        if(neighbourTile != null){
            neighbourTile.checkNeighbours();
        }
    }

    public void checkNeighbours(){
        displayTile = 0;

        if(this.tileType == null){
            return;
        }

        // Check the tile above
        MapTile neighbourTile = tileMap.getTile(xPos, yPos + 1);
        if(neighbourTile != null){
            if(neighbourTile.tileType == tileType ){
                displayTile += 1;
            }
        }

        // Check the tile below
        neighbourTile = tileMap.getTile(xPos, yPos - 1);
        if(neighbourTile != null){
            if(neighbourTile.tileType == tileType ){
                displayTile += 4;
            }
        }

        // Check the tile to the left
        neighbourTile = tileMap.getTile(xPos + 1, yPos);
        if(neighbourTile != null){
            if(neighbourTile.tileType == tileType ){
                displayTile += 2;
            }
        }

        // Check the tile to the right
        neighbourTile = tileMap.getTile(xPos - 1, yPos);
        if(neighbourTile != null){
            if(neighbourTile.tileType == tileType ){
                displayTile += 8;
            }
        }
    }

    public void renderTile(SpriteBatch batch){
        if(tileType != null){
            batch.draw(
                    tileType.tileVariations[displayTile],
                    xPos * tileMap.tileSize,
                    yPos * tileMap.tileSize,
                    tileMap.tileSize,
                    tileMap.tileSize
            );
        }
    }
}
\$\endgroup\$
0
\$\begingroup\$

I am also picking tile variations based on a tile's eight neighbors. Encoding the neighborhood is fairly simple, but deciding what pattern each set of bits represents is kind of tricky. I currently just have collections of matching ints for each pattern, and I am checking each pattern type for containment.

I do have the impression that a Binary Trie could be used for fast pattern matching. For example if you changed the encoding order of the neighbors you could arrange for matching configurations to all have the same bit-prefix.

You can read more about what I came up with so far in this blog post.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.