From ed9e5a394fa41758b739e7298852f1c9e2f70a03 Mon Sep 17 00:00:00 2001 From: Lucas Fryzek Date: Fri, 20 Sep 2024 08:38:26 +0100 Subject: bsp_level_generator: Switch to grid based approach Generate levels on grid and use grid to place doors. Basic POC of BSP levels is now working. --- scripts/bsp_level_generator.gd | 110 ++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 46 deletions(-) (limited to 'scripts') diff --git a/scripts/bsp_level_generator.gd b/scripts/bsp_level_generator.gd index b6cb316..7d9e268 100644 --- a/scripts/bsp_level_generator.gd +++ b/scripts/bsp_level_generator.gd @@ -12,6 +12,7 @@ extends Node3D var min_room_size: int = min_dim * min_dim enum Direction {LEFT, RIGHT} +enum Tile {FLOOR, WALL} class BSPNode: var axis: int @@ -57,59 +58,76 @@ func generate_level(axis: int, min_space: Vector2i, max_space: Vector2i, depth: func is_leaf(node: BSPNode) -> bool: return node.left == null and node.right == null - -func get_children_on_axis(node: BSPNode, axis: int, value: int) -> Array[BSPNode]: - if is_leaf(node): - if node.min_dims[axis] == value or node.max_dims[axis] == value: - return [node] - else: - return [] - else: - return get_children_on_axis(node.left, axis, value) + get_children_on_axis(node.right, axis, value) -func generate_doors(node: BSPNode): - var left_children = get_children_on_axis(node.left, node.axis, node.left.max_dims[node.axis]) - var right_children = get_children_on_axis(node.right, node.axis, node.right.min_dims[node.axis]) - - print(left_children) - print(right_children) - -func generate_geo(map: BSPNode): - # TODO figure out where to place walls - if map.left == null and map.right == null: - var box = CSGBox3D.new() - var p1 = Vector3(map.min_dims.x + wall_thickness/2, 0, map.min_dims.y + wall_thickness/2) - var p2 = Vector3(map.max_dims.x - wall_thickness/2, 0.1, map.max_dims.y - wall_thickness/2) - box.size = p2 - p1 - var pos_p1 = Vector3(map.min_dims.x, 0, map.min_dims.y) - var pos_p2 = Vector3(map.max_dims.x, 0, map.max_dims.y) - var offset = (pos_p2 - pos_p1) / 2 - box.position = p1 + offset - Vector3(0, 1, 0) - add_child(box) +func generate_grid(map: BSPNode, grid: Array[Tile]): + if is_leaf(map): + for y in range(map.min_dims.y + 1, map.max_dims.y - 1): + for x in range(map.min_dims.x + 1, map.max_dims.x - 1): + grid[y * width + x] = Tile.FLOOR else: - generate_geo(map.left) - generate_geo(map.right) - - # Generate the wall between the two - var wall = CSGBox3D.new() - var axis = 0 if map.axis == 0 else 2 - var other_axis = 2 if axis == 0 else 0 - var max_dim = Vector3(map.max_dims.x, 0, map.max_dims.y) - var min_dim = Vector3(map.min_dims.x, 0, map.min_dims.y) - wall.size[map.axis] = wall_thickness - wall.size[other_axis] = max_dim[other_axis] - min_dim[other_axis] - wall.size[1] = 2 + generate_grid(map.left, grid) + generate_grid(map.right, grid) - var pos2d = map.left.max_dims - var pos3d = Vector3(pos2d.x, 0, pos2d.y) - wall.position = pos3d - (wall.size/2) + Vector3(1, 1, 1) - add_child(wall) + # Look for space on dividing wall to place door + var other_axis = (map.axis + 1) % 2 + var split_axis = map.left.max_dims[map.axis] + var have_door = false + var tries = 0 + while not have_door: + var test_door = randi_range(map.min_dims[other_axis], map.max_dims[other_axis] - 1) + var door_pos = Vector2i.ZERO + door_pos[map.axis] = split_axis + door_pos[other_axis] = test_door + + var room_left = door_pos + room_left[map.axis] -= 2 + + var room_right = door_pos + room_right[map.axis] += 1 + + # Check if there are two spaces to connect + # And ensure no door already exists in the space + if grid[room_left.y * width + room_left.x] == Tile.FLOOR \ + and grid[room_right.y * width + room_right.x] == Tile.FLOOR \ + and grid[door_pos.y * width + door_pos.x] == Tile.WALL: + have_door = true + grid[door_pos.y * width + door_pos.x] = Tile.FLOOR + door_pos[map.axis] -= 1 + grid[door_pos.y * width + door_pos.x] = Tile.FLOOR + + tries += 1 + if tries > 1000: + print("Took too many attempts to generate a door") + get_tree().quit() + +func generate_geo(grid: Array[Tile]): + var csg_root = CSGCombiner3D.new() + for y in range(height): + for x in range(width): + var tile = grid[y * width + x] + + if tile == Tile.FLOOR: + var box = CSGBox3D.new() + box.size = Vector3(1.5, 0.1, 1.5) + box.position = 1.5*Vector3(x, 0, y) + 0.5 * box.size + csg_root.add_child(box) + elif tile == Tile.WALL: + var box = CSGBox3D.new() + box.size = Vector3(1.5, 2, 1.5) + box.position = 1.5*Vector3(x, 0, y) + 0.5 * box.size + csg_root.add_child(box) + + csg_root.use_collision = true + add_child(csg_root) func _ready() -> void: var starting_axis = randi_range(0, 1) var min_space = Vector2i(0, 0) var max_space = Vector2i(width, height) var map = generate_level(starting_axis, min_space, max_space) - generate_doors(map) - generate_geo(map) + var grid: Array[Tile] = [] + grid.resize(width * height) + grid.fill(Tile.WALL) + generate_grid(map, grid) + generate_geo(grid) -- cgit