BSP generated map, issues consistently spawning player on floor spaces

Godot Version



Hello everyone! Beginner Godot user, and extremely novice programmer here. I’ve finally been dabbling in 2D game design recently, and I am currently experimenting with procedural generation using the BSP generation method (I’ve always been fascinated with procedural generation). After following a few tutorials I was able to get something that works about 80% as intended. The map generates properly, and I can randomly place clutter around on the floor tiles of my map. The one issue I can’t seem to overcome is getting my player instance to spawn reliably on a floor tile in a randomly chosen room. Sometimes my player will spawn on a wall tile. Ultimately what I’m asking is, what is the proper way to store each BSP “leaf” as its own grid, and then access that grid, find its center, and then spawn the player instance there?

Apologies in advance for the bulky code.

This is the function that handles placing the floor tiles in the leaves.

func draw_rooms():
	for leaf in root_node.get_leaves():
		var padding = Vector4i(
			rng.randi_range(1, 2),
			rng.randi_range(1, 2),
			rng.randi_range(1, 2),
			rng.randi_range(1, 2)
		for x in range(leaf.size.x):
			for y in range(leaf.size.y):
				if not is_inside_padding(x, y, leaf, padding):
					tilemap.set_cell(0, Vector2i(x + leaf.position.x, y + leaf.position.y), 0, Vector2i(0, 1))
					rooms_pos.append(Vector2i(x + leaf.position.x, y + leaf.position.y)) 
					rooms_size.append(Vector2i(x + leaf.size.x, y + leaf.size.y))

This function places my clutter textures. This is working more or less as intended.

func spawn_cluter():
	var clutter_chance = 0.02
	var clutter_tileset_id = 1
	for room_position in rooms_pos:
		if room_position != player_pos:
			for x_offset in [-1, 0, 1]:
				for y_offset in [-1, 0, 1]:
					var adjacent_position = room_position + Vector2i(x_offset, y_offset)
					if tilemap.get_cell_atlas_coords(0, adjacent_position) != tilemap.get_cell_atlas_coords(0, Vector2i(0,0)):
						if adjacent_position.x >= 0 and adjacent_position.x < map_size.x and adjacent_position.y >= 0 and adjacent_position.y < map_size.y:
							if rng.randf() <= clutter_chance:
#								tilemap.set_cell(1, adjacent_position, clutter_tileset_id, Vector2i(randi_range(0, 3), randi_range(0, 6))) #OG clutter system
								var clutter_type = rng.randf_range(0.0, 100.0)
								if clutter_type <= 10.0: # Chest
									tilemap.set_cell(1, adjacent_position, clutter_tileset_id, Vector2i(3, 6))
								elif clutter_type <= 20.0: # Skull
									tilemap.set_cell(1, adjacent_position, clutter_tileset_id, Vector2i(rng.randi_range(2, 3), 6))
								elif clutter_type <= 35.0: # Blood
									tilemap.set_cell(1, adjacent_position, clutter_tileset_id, Vector2i(rng.randi_range(0, 3), 3))
								elif clutter_type <= 60.0: # Barrels/crates
									tilemap.set_cell(1, adjacent_position, clutter_tileset_id, Vector2i(rng.randi_range(0, 3), rng.randi_range(0, 1)))
								else: # clutter_type <= 80.0: # Cracks
									tilemap.set_cell(1, adjacent_position, clutter_tileset_id, Vector2i(rng.randi_range(0, 3), 4))

This script is what sets up the logic for splitting the rooms. I’m not sure if this is relevant to the issue I’m having, but thought it would help with understanding the overall premise of what is going on.

extends Node

class_name Branch

var left_child: Branch
var right_child: Branch
var position: Vector2i
var size: Vector2i

func _init(position, size) -> void:
	self.position = position
	self.size = size
func get_leaves():
	if not (left_child && right_child):
		return [self]
		return left_child.get_leaves() + right_child.get_leaves()
func split(remaining, paths: Array):
	var rng =
	var split_percent = rng.randf_range(0.3, 0.7)
	var split_horizontal = size.y >= size.x 
	if (split_horizontal):
		# Horizontal
		var left_height = int(size.y * split_percent)
		left_child =, Vector2i(size.x, left_height))
		right_child =
			Vector2i(position.x, position.y + left_height),
			Vector2i(size.x, size.y - left_height)
		# Vertical
		var left_width = int(size.x * split_percent)
		left_child =, Vector2i(left_width, size.y))
		right_child =
			Vector2i(position.x + left_width, position.y),
			Vector2i(size.x - left_width, size.y)
	paths.push_back({'left': left_child.get_center(), 'right': right_child.get_center()})
	if (remaining > 0):
		left_child.split(remaining - 1, paths)
		right_child.split(remaining - 1, paths)

func get_center():
	return Vector2i(position.x + size.x / 2,position.y + size.y / 2)

To sum up, I want to make sure when my player spawns, it is in one of the rooms, and not in the wall, preferably in or near the center of the room. Also, I want to know what the right way to store the information I need to do things like this down the road. Thanks in advance!