Godot Version
4.1.3.stable
Question
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]
else:
return left_child.get_leaves() + right_child.get_leaves()
func split(remaining, paths: Array):
var rng = RandomNumberGenerator.new()
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 = Branch.new(position, Vector2i(size.x, left_height))
right_child = Branch.new(
Vector2i(position.x, position.y + left_height),
Vector2i(size.x, size.y - left_height)
)
else:
# Vertical
var left_width = int(size.x * split_percent)
left_child = Branch.new(position, Vector2i(left_width, size.y))
right_child = Branch.new(
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!