Skip to main content
Coding Quests
The Scroll Library
Visual Explainers

How to Generate Walls Around Dungeon Floors

October 31, 20268 min read
Part 5 of 8: Procedural dungeons, explained

How a dungeon generator places rooms, joins them, builds walls, picks the boss room and proves the result is playable. Every part uses the same seeded 22 by 22 dungeon.

  1. 1.How dungeon generation works
  2. 2.Stopping rooms overlapping
  3. 3.Making every room reachable
  4. 4.Joining premade rooms at their doors
  5. 5.Walls around the floor
  6. 6.Where the boss room goes
  7. 7.Seeds: the same dungeon on purpose
  8. 8.Testing that a level is playable

Your generator carves a beautiful floor plan, and then you have to put walls around it. Do it by hand and you miss a corner. Do it with a quick rule and the corners of every room come out notched, or the walls overlap the floor, or one tile in a doorway is a wall when it should not be.

Walls are not something you generate separately. They follow from the floor. This is part 5 of a series that builds one dungeon generator, pass by pass.

Walls come from the floor

After the room and corridor passes, every cell is either floor or rock. A wall is just the rock that the player can see, the rock that touches floor. Rock deeper in never shows, so it never needs a tile.

That gives the whole rule: a rock cell becomes a wall when one of its neighbours is floor. The only question is which cells count as neighbours.

Four neighbours or eight?

The 4 neighbours of a cell are the ones that share a side with it: left, right, up and down. The 8 neighbours add the four diagonals, the cells that only touch at a corner.

Look at the outside corner of a rectangular room. The rock cell just beyond the corner touches the floor only diagonally. With 4 neighbours it has no floor neighbour, so it stays rock. Every room ends up with a notch at each outside corner, a gap in the wall line where you can see straight into the dark.

With 8 neighbours that corner cell sees the floor diagonally and becomes a wall. The wall line closes. Inspect the corner cells in the demo, then switch rules.

Walls from floor: the neighbour test

Interactive, engine-accurate

Cell (7, 6) is rock with 1 floor neighbour out of 8, so it becomes a wall.

?

The orange square is the cell being inspected, and the outlined cells are the floor neighbours it found. This rule turned 48 rock cells into walls. Look at the outside corners of each room. With 4 neighbours the corner cell only touches floor diagonally, so it stays rock and the wall line has a notch. Focus the map to use the arrow keys, and Space to paint.

This runs the GDScript from this article, ported to the browser. The same seed gives the same result in Godot 4.7.2.

On seed 214, the dungeon from this series, 4 neighbours makes 158 wall cells and 8 neighbours makes 179. The 21 extra cells are all corners.

When the notch matters

In a game with only straight moves, the notch is cosmetic. The player can never step diagonally, so they can never squeeze between two wall corners. But it still shows: if your rock is drawn as black void, every corner reads as a hole in the wall.

If your player or enemies can move diagonally, it is a real gap. Two walls that touch only at their corners leave a diagonal path between them, and pathfinding will happily take it. Use 8 neighbours for tile walls unless you have a reason not to.

Edge walls for 3D kits

Modular 3D kits, like KayKit's dungeon pieces, do not fill cells with walls. A wall piece is a thin slab that stands on the edge between two cells. For those, turn the rule around:

Visit every floor cell. For each of its 4 sides, if the cell on the other side is not floor, put a wall piece on that side, facing in.

Corners need no extra rule here. Two wall pieces meet at the corner of a floor cell on their own. Diagonals do not matter either, because a slab only ever sits on a side. On seed 214 this places 208 wall pieces. Pick Edge pieces in the demo to see them as thick lines on cell edges.

ApproachVisitsChecksGood for
Tile walls, 4 neighboursEvery rock cellLeft, right, up, downCheap, but notched corners
Tile walls, 8 neighboursEvery rock cellAll 8 around it2D tile maps, closed corners
Edge wallsEvery floor cellIts 4 sidesModular 3D wall pieces

Where it still goes wrong

Walls written into the floor. If your wall pass reads the grid while also writing walls into it, a new wall can make the next cell think it touches something solid. Write walls to rock cells only, and only test against floor, as below. Then the order cells are visited in does not matter.

Rooms at the edge of the map. A floor cell on the very edge has no room outside it for a wall. Keep a one-cell margin of rock around the map when you place rooms. The generator in this series rolls positions starting at 1 for that reason.

Two-tile-high walls. In top-down art with a visible wall face, a wall cell with floor directly below it needs the face tile, and every other wall cell needs the top. That is one more neighbour check on the cells your wall pass already found. Godot's terrain system can also pick the right tile for you: TileMapLayer.set_cells_terrain_connect() "might update neighboring tiles if needed to create correct terrain transitions", in the docs' words.

Doors. A doorway is floor, so nothing in this pass will put a wall there. Doors that close are a separate object placed on top, after walls. Lesson 11 of the 3D dungeon course closes the boss room's doorway with a gate piece this way.

In Godot: the wall pass

The tile version visits every cell, skips anything that is not rock, and turns rock into wall as soon as one neighbour is floor:

GDScript
func build_walls(include_diagonals: bool = true) -> void:
var offsets := NEIGHBORS_8 if include_diagonals else NEIGHBORS_4
for y in grid_height:
for x in grid_width:
var cell := Vector2i(x, y)
if grid[index(cell)] != EMPTY:
continue
for offset in offsets:
if is_floor(cell + offset):
grid[index(cell)] = WALL
break

NEIGHBORS_8 is a list of the 8 offsets around a cell, from Vector2i(-1, -1) to Vector2i(1, 1). is_floor() returns false for cells off the edge of the grid, so the border needs no special case. Because the test only asks "is this floor?", walls already written never affect later cells.

The edge version returns a list of wall pieces: the floor cell, and the direction the piece faces away from it:

GDScript
func wall_edges() -> Array[Dictionary]:
var edges: Array[Dictionary] = []
for y in grid_height:
for x in grid_width:
var cell := Vector2i(x, y)
if not is_floor(cell):
continue
for dir in NEIGHBORS_4:
if not is_floor(cell + dir):
edges.append({"cell": cell, "facing": dir})
return edges

To draw tile walls, give your tileset a floor tile and a wall tile and call set_cell() once per cell:

GDScript
func paint_tiles(layout: Object, layer: TileMapLayer, source_id: int) -> void:
layer.clear()
for y in layout.grid_height:
for x in layout.grid_width:
var cell := Vector2i(x, y)
var kind: int = layout.grid[layout.index(cell)]
if kind == layout.FLOOR:
layer.set_cell(cell, source_id, FLOOR_TILE)
elif kind == layout.WALL:
layer.set_cell(cell, source_id, WALL_TILE)

source_id is the id of the atlas in your TileSet, and FLOOR_TILE and WALL_TILE are the atlas coordinates of the two tiles. For edge walls in 3D, instance your wall scene once per entry in wall_edges(). Place it on the shared side of the cell, and turn it to face along facing.

The complete generator is dungeon_layout.gd.

Build it next

Procedural Dungeon & Run

A quest on CodingQuests: 12 interactive lessons that build one part of a game in Godot, in your browser, checked as you go.

Lesson 4 of Procedural Dungeon & Run is the edge-wall pass in 3D. It checks the 4 sides of every floor cell and spawns a wall piece from the KayKit set, plus a box collider so the player cannot walk through. Lesson 5 fills the rock between rooms with solid stone, so nothing falls off the map.

Both are member lessons. The course builds edge walls only. Use the 8-neighbour tile rule above for a 2D tile map.

  • Lesson 4: Walls Around the RoomsMembers
  • Lesson 5: No More Falling OffMembers

Lessons 1 to 3 are free to open, no account needed. Guild Hall, the membership, opens the other 9.

FAQ

How do I add walls around a procedurally generated floor in Godot?

After the floor is carved, loop over every cell. Any empty cell with a floor neighbour becomes a wall, and you paint it with TileMapLayer.set_cell() using your wall tile. Check all 8 neighbours so room corners close.

Why do my generated walls have gaps at the corners?

Your wall check only looks at 4 neighbours. The cell outside a room's corner touches the floor only diagonally, so a 4-way check leaves it empty. Include the 4 diagonal neighbours in the check.

Should walls be tiles or separate wall pieces?

For 2D tile maps, make walls tiles in their own cells. For modular 3D kits, put wall pieces on the edges of floor cells instead. Both come from the same floor grid. Only the neighbour rule changes.

Sources and testing

How this was tested. The GDScript on this page was run in Godot 4.7.2 (headless, on Windows) on 31 October 2026. Painting seed 214 into a real TileMapLayer gave 190 floor tiles and 179 wall tiles, the same counts as the layout. The demo runs a browser port of the same wall code, checked against Godot on 40 seeds. The two-tile-high face rule and set_cells_terrain_connect() are described, not tested here.

procedural generationdungeontilemaplayergridmapgodot

Stop reading. Start building.

Build a roguelike in Godot from the first lesson. The first quest, the player controller, is free from start to finish. Later quests in the same game add combat, enemies, rooms, loot and upgrades.

Free, and no card needed. Built by a real person, with new quests every month.

Get the next Godot build in your inbox

New quests, project breakdowns, and game-dev tips.

You'll get emails from CodingQuests with Godot tips, new tutorials, course updates, and occasional offers. Unsubscribe anytime. Privacy Policy

Written by Omar

We teach Godot 4 by making you build complete systems: inventories, save systems, action roguelike controllers, enemy AI. The scrolls are free. The quests are where it sticks.