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.
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 @anon73286167!
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 
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 
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)