Walker dungeon generation wrong way around

Godot Version

Godot 4.2

Question

Currently trying to make a 2.5d dungeon crawler game to learn.
I am following along with Heartbeast dungeon generation video. But for some reason, the tiles where the dungeon should be are empty and the tiles that should be empty are dungeon tiles.


Here is the dungeon generation code part.

extends Node2D

var borders = Rect2(1, 1, 32, 32)

@onready var tileMap = $TileMap

func _ready():
	generate_level()

func generate_level():
	var walker = Walker.new(Vector2(16, 16), borders)
	var map = walker.walk(200)
	walker.queue_free()
	for location in map:
		print(location)
		tileMap.set_cell(0, location)
	tileMap.set_cells_terrain_connect(0, map, 0, -1)

Have been trying to fix it by myself for close to 2h now.
The set_cell and set_cells_terrain_connect part is taken from this reddit post because original video uses set_cellv and update_bitmask_region.
Thank you to whoever will help me out.

I’m not sure, but is the layer 0 of the tilemap a “white” space, and layer 1 the building blocks?

1 Like

Currently, I have Terrain set 0 as building blocks and I don’t have a separate Terrain set for empty tile.

I'm deeply sorry, it is in the video!

could you point out o the Godot docs a reference to the Walker class? I was going to examine it, but couldn’t find on https://docs.godotengine.org/

1 Like

could you show what location prints? way better, its first print;

it seems simply your walker is walking straight into where the level should be, as the function puts a block where the walker walks

1 Like

Yes, the walker class is in the video and the location will print out the place that is empty currently.

the best hackery I can think of is creating another layer, blank, no collision, and set the tilemap to full blocks, and change the function to set_cell(1, location)

that doesn’t teach much tho on how it works…

1 Like

I maybe a better way would be to get every location in the tilemap, so something like:

for loc in tilemap:
  for location in map:
    if loc != location:
      set_cell(0, location)
1 Like

The set_cell(1, locations) did the same thing.
And for loc in tileMap gave me an error:


Also not gonna deal with today anymore, will be back tomorrow to see if there is anything new. Thank you for everything so far!

1 Like

I’m sorry, that was just an unusable example, not a plain copy/paste solution.

loc should be every position in a tilemap, now, how you get that, is unknown for me

hey, is your map 32 by 32?

then you could get every location in it by:

allLocations = []
for n in 32:
  for i in 32:
    allLocations += Vector2i(n, i)

maybe you should do allLocations += [Vector2i(n, i)] instead of allLocations += Vector2i(n, i), I am not sure, then in my previous code example, tilemap would be called allLocations instead

1 Like

Even tho I said I won’t touch it again today I still did AND IT NOW WORKS!

Thank you @undone!

The final code looks like this:

extends Node2D

var borders = Rect2(0, 0, 32, 32)

@onready var tileMap = $TileMap

func _ready():
	generate_level()

func generate_level():
	var walker = Walker.new(Vector2(0, 0), borders)
	var map = walker.walk(200)
	walker.queue_free()
	var allLocations = []
	for n in 32:
		for i in 32:
			allLocations.append(Vector2(n, i)) 
	for loc in allLocations:
		for location in map:
			if loc != location:
				tileMap.set_cell(0, loc)
	tileMap.set_cells_terrain_connect(0, map, 0, 0)

And just if anyone wants the walker class code:

extends Node
class_name Walker

const DIRECTIONS = [Vector2.RIGHT, Vector2.UP, Vector2.LEFT, Vector2.DOWN]

var position = Vector2.ZERO
var direction = Vector2.RIGHT
var borders = Rect2()
var step_history = []
var steps_since_turn = 0

func _init(starting_position, new_border):
	assert(new_border.has_point(starting_position))
	position = starting_position
	step_history.append(position)
	borders = new_border

func walk(steps):
	create_room(position)
	for step in steps:
		if steps_since_turn >= 6:
			change_direction()
		if step():
			step_history.append(position)
		else:
			change_direction()
	return step_history

func step():
	var target_position = position + direction
	if borders.has_point(target_position):
		steps_since_turn += 1
		position = target_position
		return true
	else:
		return false

func change_direction():
	create_room(position)
	steps_since_turn = 0
	var directions = DIRECTIONS.duplicate()
	directions.erase(direction)
	directions.shuffle()
	direction = directions.pop_front()
	while not borders.has_point(position + direction):
		direction = directions.pop_front()

func create_room(position):
	var size = Vector2(randi() % 5 + 2, randi() % 5 + 2)
	var top_left_corner = (position - size/2).ceil()
	for y in size.y:
		for x in size.x:
			var new_step = top_left_corner + Vector2(x, y)
			if borders. has_point(new_step):
				step_history.append(new_step)

very small tip: you may name n to x and i to y, just for clarity when reading the code :slight_smile:

also, you may create a Vector2i() with the size of your map, then using thar vector2i’s x and y to change automatically both the size of your map, and the function which counts its place :slight_smile:

even better if it is an exported variable with @export

ok, so I figured what is going on and how to not do in the hacky way, which is broken anyway:

the third parameter in set_cell() needs to be the block you are generating, and thed default is -1, which means delete, so scrap the code I showed and do this:

func generate_level():
	var walker = Walker.new(Vector2(16, 16), borders)
	var map = walker.walk(200)
	walker.queue_free()
	for location in map:
		print(location)
		tileMap.set_cell(0, location, 0)
	tileMap.set_cells_terrain_connect(0, map, 0, -1)

(the difference is 0 at th end of set_cell())

this is cleaner and faster code, also, it allows for you to set-up the level with different kinds of blocks

updating:

tileMap.set_cell(LAYER_TO_PUT_THE_CELL, PLACE_TO_PUT_IT, WHICH_ATLAS_ID, WHICH_BLOCK_FROM_ATLAS)

so if you want to put the block (1,0) at layer 1, at position (5, 5), from the atlas 0, you would do:

tileMap.set_cell(1, Vector2i(5, 5), 0, Vector2i(1, 0))

so in your example, it would be:

func generate_level():
	var walker = Walker.new(Vector2(16, 16), borders)
	var map = walker.walk(200)
	walker.queue_free()
	for location in map:
		print(location)
		tileMap.set_cell(0, location, 0, Vector2i(0, 0))
	tileMap.set_cells_terrain_connect(0, map, 0, -1)