From 91f52909087dca648df82ea7fe77c2348a18c06f Mon Sep 17 00:00:00 2001 From: Lucas Fryzek Date: Thu, 19 Sep 2024 21:41:34 +0100 Subject: bsp_level_generator: Add 3D floor and wall generation Still need to figure out how to place and generator doors between rooms --- prefabs/bsp_level_generator.tscn | 4 +- prefabs/test_arena.tscn | 10 ++--- scripts/bsp_level_generator.gd | 91 ++++++++++++++++++++++++++++------------ 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/prefabs/bsp_level_generator.tscn b/prefabs/bsp_level_generator.tscn index 9b3c198..dbb62a5 100644 --- a/prefabs/bsp_level_generator.tscn +++ b/prefabs/bsp_level_generator.tscn @@ -2,7 +2,5 @@ [ext_resource type="Script" path="res://scripts/bsp_level_generator.gd" id="1_6jn1x"] -[node name="BspLevelGenerator" type="Node2D"] +[node name="BspLevelGenerator" type="Node3D"] script = ExtResource("1_6jn1x") - -[node name="Camera2D" type="Camera2D" parent="."] diff --git a/prefabs/test_arena.tscn b/prefabs/test_arena.tscn index 7b7a510..b5e45db 100644 --- a/prefabs/test_arena.tscn +++ b/prefabs/test_arena.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=9 format=3 uid="uid://dlevowk0jrhlg"] +[gd_scene load_steps=10 format=3 uid="uid://dlevowk0jrhlg"] [ext_resource type="PackedScene" uid="uid://cc1m2a1obsyn4" path="res://addons/fpc/character.tscn" id="1_vc6b5"] [ext_resource type="Script" path="res://scripts/player.gd" id="2_puqns"] [ext_resource type="PackedScene" uid="uid://cuad3khwmhnsa" path="res://prefabs/enemy.tscn" id="3_4ykmj"] +[ext_resource type="PackedScene" uid="uid://w7hxcvuvud" path="res://prefabs/bsp_level_generator.tscn" id="4_501hd"] [sub_resource type="Environment" id="Environment_2poci"] background_color = Color(0.130548, 0.170599, 0.433834, 1) @@ -79,6 +80,7 @@ transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, [node name="Player" parent="." node_paths=PackedStringArray("HEAD", "HEADBOB_ANIMATION") instance=ExtResource("1_vc6b5")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2.97532, -1.28752, 0.602886) +motion_mode = 1 HEAD = NodePath("PlayerCam") HEADBOB_ANIMATION = NodePath("PlayerCam/HeadAnimation") LEFT = "left" @@ -89,6 +91,7 @@ jumping_enabled = false in_air_momentum = false sprint_enabled = false crouch_enabled = false +gravity_enabled = false [node name="PlayerCam" type="Node3D" parent="Player"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0) @@ -114,7 +117,4 @@ motion_mode = 0 target = NodePath("../Player") gravity_enabled = true -[node name="CSGBox3D" type="CSGBox3D" parent="."] -use_collision = true -flip_faces = true -size = Vector3(20, 4, 20) +[node name="BspLevelGenerator" parent="." instance=ExtResource("4_501hd")] diff --git a/scripts/bsp_level_generator.gd b/scripts/bsp_level_generator.gd index af34e0b..b6cb316 100644 --- a/scripts/bsp_level_generator.gd +++ b/scripts/bsp_level_generator.gd @@ -1,20 +1,27 @@ -extends Node2D +extends Node3D +@export_category("Level Generator") @export var width: int = 40 @export var height: int = 40 @export var min_dim: int = 5 +@export_category("Geometry Generator") +## Geometry generation config +@export var wall_thickness: float = 1 + var min_room_size: int = min_dim * min_dim -var rects: Array[Vector4i] = [] +enum Direction {LEFT, RIGHT} class BSPNode: + var axis: int var min_dims: Vector2i var max_dims: Vector2i var left: BSPNode var right: BSPNode - func _init(p_min_dims: Vector2i, p_max_dims: Vector2i, p_left: BSPNode, p_right: BSPNode): + func _init(p_axis: int, p_min_dims: Vector2i, p_max_dims: Vector2i, p_left: BSPNode, p_right: BSPNode): + axis = p_axis min_dims = p_min_dims max_dims = p_max_dims left = p_left @@ -22,24 +29,19 @@ class BSPNode: func generate_level(axis: int, min_space: Vector2i, max_space: Vector2i, depth: int = 0) -> BSPNode: var dims = max_space - min_space - if dims[axis] / 2 < min_dim: - #rects.append(Vector4i(min_space.x, min_space.y, max_space.x, max_space.y)) - return null - var new_axis = (axis + 1) % 2 # 10% we stop here and just create a big room if (depth > 2 and randi_range(0, 9) == 0) \ or dims.x * dims.y <= min_dim * min_dim \ or dims[new_axis] / 2 < min_dim: - rects.append(Vector4i(min_space.x, min_space.y, max_space.x, max_space.y)) - return BSPNode.new(min_space, max_space, null, null) + return BSPNode.new(new_axis, min_space, max_space, null, null) # Calculate min and max ranges so that a split # doesn't create a room that violates min dimensions var min_value = min_space[axis] + min_dim var max_value = max_space[axis] - min_dim var split = randi_range(min_value, max_value) - print("Spliting axis ", axis, " at ", split) + #print("Spliting axis ", axis, " at ", split) var left_min_space = min_space var left_max_space = max_space @@ -51,28 +53,63 @@ func generate_level(axis: int, min_space: Vector2i, max_space: Vector2i, depth: var right_max_space = max_space var right = generate_level(new_axis, right_min_space, right_max_space, depth + 1) - assert((left == null and right == null) or (left != null and right != null)) + return BSPNode.new(axis, min_space, max_space, left, right) - if left == null and right == null: - rects.append(Vector4i(min_space.x, min_space.y, max_space.x, max_space.y)) +func is_leaf(node: BSPNode) -> bool: + return node.left == null and node.right == null - return BSPNode.new(min_space, max_space, left, right) +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) + 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 + + 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) + 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) - -func _draw(): - var mult = 1 - for rect in rects: - var pos1 = 5*Vector2i(rect.x, rect.y) - var pos2 = 5*Vector2i(rect.z, rect.w) - var dims = pos2 - pos1 - draw_rect(Rect2(pos1, dims), mult*Color(0.01, 0.01, 0.01)) - draw_line(pos1, Vector2i(pos1.x, pos2.y), Color(0, 1, 0)) - draw_line(Vector2i(pos1.x, pos2.y), pos2, Color(0, 1, 0)) - draw_line(pos2, Vector2i(pos2.x, pos1.y), Color(0, 1, 0)) - draw_line(Vector2i(pos2.x, pos1.y), pos1, Color(0, 1, 0)) - mult += 2 + generate_doors(map) + generate_geo(map) -- cgit