Quick answer: Make sure your terrain set’s mode matches your tile layout (use “Match Corners and Sides” for standard terrain tiles), verify that every peering bit on every tile is assigned to the correct terrain, and check that you haven’t left any peering bits unset — unset bits act as “don’t care” and cause unexpected matches.
You’ve painstakingly set up terrain tiles in Godot’s TileSet editor, configured what you think are the right peering bits, and started painting on your TileMap. But the autotiler keeps placing the wrong tiles — straight edges where there should be corners, grass tiles in the middle of water, or completely blank cells where a tile should appear. The terrain system in Godot 4.x is powerful, but its peering bit configuration is unforgiving when even one bit is wrong.
Understanding Terrain Modes
The first thing to check is your terrain set’s mode. This setting is in the TileSet editor under the Terrains tab, and it determines which peering bits the autotiler considers when matching tiles. There are three modes:
- Match Corners and Sides: Considers all 8 peering bits (4 sides + 4 corners). This is the standard mode for RPG-style terrain where tiles have distinct edge and corner pieces. Most terrain tilesets from asset stores are designed for this mode.
- Match Corners: Only considers the 4 corner peering bits. Used for simpler terrain systems where only the corners determine tile selection, common in top-down games with blob-style autotiling.
- Match Sides: Only considers the 4 side peering bits. Used for Wang tile sets and some stylized terrain approaches.
If your tileset was designed for 8-bit (corners and sides) autotiling but you’ve set the mode to “Match Sides,” the engine will ignore all corner peering bits. This means tiles with different corner configurations will be treated as identical, and the autotiler will pick randomly between them. The result looks like a jumbled mess of correct and incorrect corner pieces.
Check your tileset’s documentation or count the tiles: a full 8-bit terrain set has 47 unique tiles (or 48 with an empty center). A 4-bit side-only set has 16 tiles. If your tileset has around 47 tiles, use “Match Corners and Sides.”
Auditing Peering Bits
The most tedious but important step is verifying that every peering bit on every tile is correct. In the TileSet editor, select a tile, go to the Terrains section in the paint properties, and check each directional bit.
Common peering bit mistakes:
Unset bits: An unset peering bit means “this direction doesn’t matter.” If you forget to set the “top” peering bit on a tile that should only appear when the tile above is the same terrain, the autotiler will place that tile regardless of what’s above it. Always set every peering bit to either your terrain type or leave it explicitly empty (no terrain) — never leave bits in an ambiguous state.
Swapped corners: The corner peering bits in Godot refer to the corner of the cell, not the corner of the visual tile. For a tile in the top-left corner of a terrain patch, the top-left peering bit should be set to “no terrain” (the empty area), and the bottom-right peering bit should be set to your terrain. It’s easy to get these backwards.
Wrong terrain assignment: If you have multiple terrains (grass, dirt, water), make sure each tile’s peering bits reference the correct terrain. A grass-to-dirt transition tile should have some bits set to “grass” and others to “dirt,” not all set to “grass.”
A systematic approach: start with the center tile (all bits set to your terrain), verify it works, then work outward to edges, then corners. Test each tile individually before testing complex patterns.
Debugging with a Test TileMap
When terrain matching seems broken, isolate the problem by creating a fresh TileMap node in a test scene. Paint a simple 3×3 block of terrain. If the center tile, edge tiles, and corner tiles all appear correctly, your TileSet is configured properly and the issue is with your existing TileMap data.
Existing TileMaps can get into a bad state if you change the TileSet configuration after painting. When you modify peering bits on existing tiles, the painted TileMap data isn’t automatically updated. You may need to erase and repaint affected areas.
# Script to force a terrain update on an existing TileMap
# Attach to your TileMap node and run once
func _ready():
var used_cells = get_used_cells(0) # Layer 0
for cell in used_cells:
var data = get_cell_tile_data(0, cell)
if data:
# Force terrain reconnection by setting the cell again
set_cells_terrain_connect(0, [cell], 0, 0)
print("Refreshed ", used_cells.size(), " cells")
Note: set_cells_terrain_connect re-evaluates terrain matching for the specified cells. The third parameter is the terrain set index and the fourth is the terrain index within that set.
Probability and Alternative Tiles
If your terrain tiles have visual variations (e.g., three different grass center tiles for visual variety), you need to set them up as alternative tiles with the same peering bit configuration but different probability values.
In the TileSet editor:
- Select the base tile for the variation.
- Add an alternative tile (click the + icon in the tile’s alternatives).
- Set the alternative’s art to your variation.
- Copy the exact same peering bits from the base tile to the alternative.
- Set the probability for each alternative. Higher values mean that tile is chosen more often.
A common mistake is creating separate tiles for variations instead of using the alternative tile system. Separate tiles with the same peering bits confuse the autotiler — it has no way to know they’re variations and may cycle through them unpredictably. Alternatives with probability values give you controlled randomization.
# Verify terrain configuration in code
func _ready():
var tileset = $TileMap.tile_set
var source = tileset.get_source(0) as TileSetAtlasSource
var terrain_set = 0
for y in range(source.get_atlas_grid_size().y):
for x in range(source.get_atlas_grid_size().x):
var coords = Vector2i(x, y)
if not source.has_tile(coords):
continue
var data = source.get_tile_data(coords, 0)
var terrain = data.get_terrain(terrain_set)
print("Tile ", coords, " terrain: ", terrain,
" bits: ", _get_peering_bits(data, terrain_set))
func _get_peering_bits(data: TileData, terrain_set: int) -> Dictionary:
var bits = {}
for bit in [
TileSet.CELL_NEIGHBOR_TOP_SIDE,
TileSet.CELL_NEIGHBOR_BOTTOM_SIDE,
TileSet.CELL_NEIGHBOR_LEFT_SIDE,
TileSet.CELL_NEIGHBOR_RIGHT_SIDE,
]:
bits[bit] = data.get_terrain_peering_bit(bit)
return bits
When the Editor Itself Is the Problem
Occasionally, the TileSet editor in Godot 4.x has display bugs where peering bits appear set in the UI but aren’t saved correctly to the resource file. If you’ve verified everything visually and tiles still don’t match, try these steps:
- Save the TileSet resource, close the editor, and reopen it. Check if the peering bits are still set.
- Open the
.tresfile in a text editor and search forterrain_peering_bitentries. Verify the values match what you expect. - If values are missing in the text file, the editor isn’t saving them. Try deleting the tile and recreating it, or re-import the TileSet resource.
- Check for engine version-specific bugs. Terrain peering bit serialization had known issues in some Godot 4.0.x and 4.1.x releases that were fixed in later versions.