From 599c8dd969ca3eec1767eaf3585ae9a32d23fa57 Mon Sep 17 00:00:00 2001 From: Lucas Fryzek Date: Sun, 22 Sep 2024 18:19:51 +0100 Subject: bsp_level_generator: Generate level geometry manually Don't use CSG geometry, this makes it easier to generate a collision mesh, and in the future would make it easier to have player created levels. Level generation is much faster now as well. --- prefabs/bsp_level_generator.tscn | 13 +++++++- scripts/bsp_level_generator.gd | 68 ++++++++++++++++++++++++++-------------- scripts/enemy.gd | 2 ++ 3 files changed, 58 insertions(+), 25 deletions(-) diff --git a/prefabs/bsp_level_generator.tscn b/prefabs/bsp_level_generator.tscn index ea0cfd7..f9e05db 100644 --- a/prefabs/bsp_level_generator.tscn +++ b/prefabs/bsp_level_generator.tscn @@ -1,11 +1,22 @@ -[gd_scene load_steps=3 format=3 uid="uid://w7hxcvuvud"] +[gd_scene load_steps=4 format=3 uid="uid://w7hxcvuvud"] [ext_resource type="Script" path="res://scripts/bsp_level_generator.gd" id="1_6jn1x"] +[sub_resource type="ArrayMesh" id="ArrayMesh_xgab6"] + [sub_resource type="NavigationMesh" id="NavigationMesh_q1fo6"] +geometry_parsed_geometry_type = 1 +geometry_collision_mask = 4294967041 [node name="BspLevelGenerator" type="Node3D"] script = ExtResource("1_6jn1x") +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("ArrayMesh_xgab6") + [node name="NavigationRegion3D" type="NavigationRegion3D" parent="."] navigation_mesh = SubResource("NavigationMesh_q1fo6") + +[node name="StaticBody3D" type="StaticBody3D" parent="NavigationRegion3D"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="NavigationRegion3D/StaticBody3D"] diff --git a/scripts/bsp_level_generator.gd b/scripts/bsp_level_generator.gd index 87806e2..2243481 100644 --- a/scripts/bsp_level_generator.gd +++ b/scripts/bsp_level_generator.gd @@ -7,7 +7,9 @@ extends Node3D @export_category("Geometry Generator") ## Geometry generation config -@export var wall_thickness: float = 1 +@export var wall_thickness: float = 1.25 + +@onready var mesh: MeshInstance3D = $MeshInstance3D var min_room_size: int = min_dim * min_dim @@ -95,6 +97,8 @@ func generate_grid(map: BSPNode, grid: Array[Tile]): and grid[room_right.y * grid_width + room_right.x] == Tile.FLOOR \ and grid[door_pos.y * grid_width + door_pos.x] == Tile.WALL: have_door = true + + # Place grid for mesh grid[door_pos.y * grid_width + door_pos.x] = Tile.FLOOR door_pos[map.axis] -= 1 grid[door_pos.y * grid_width + door_pos.x] = Tile.FLOOR @@ -105,34 +109,37 @@ func generate_grid(map: BSPNode, grid: Array[Tile]): get_tree().quit() return -func generate_geo(grid: Array[Tile]): - var csg_root = CSGCombiner3D.new() +func generate_plane(array: Array, pos: Vector3, dim: Array[Vector3], normal: Vector3): + var index = len(array[Mesh.ARRAY_VERTEX]) + array[Mesh.ARRAY_VERTEX].append(wall_thickness*pos) + array[Mesh.ARRAY_VERTEX].append(wall_thickness*(pos+dim[0])) + array[Mesh.ARRAY_VERTEX].append(wall_thickness*(pos+dim[0]+dim[1])) + array[Mesh.ARRAY_VERTEX].append(wall_thickness*(pos+dim[1])) + + array[Mesh.ARRAY_NORMAL].append(normal) + array[Mesh.ARRAY_NORMAL].append(normal) + array[Mesh.ARRAY_NORMAL].append(normal) + array[Mesh.ARRAY_NORMAL].append(normal) + + array[Mesh.ARRAY_INDEX].append(index + 0) + array[Mesh.ARRAY_INDEX].append(index + 1) + array[Mesh.ARRAY_INDEX].append(index + 2) + array[Mesh.ARRAY_INDEX].append(index + 0) + array[Mesh.ARRAY_INDEX].append(index + 2) + array[Mesh.ARRAY_INDEX].append(index + 3) + +func generate_geo(grid: Array[Tile], array: Array): for y in range(grid_height): for x in range(grid_width): var tile = grid[y * grid_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) + generate_plane(array, Vector3(x, 0, y), [Vector3(1, 0, 0), Vector3(0, 0, 1)], Vector3.UP) 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 - $NavigationRegion3D.add_child(csg_root) - - # We need to delay baking the nav mesh as - # the CSG won't be generated immediately - call_deferred("bake_nav") - -func bake_nav(): - print("Baking mesh") - $NavigationRegion3D.bake_navigation_mesh(false) - print("done baking") + generate_plane(array, Vector3(x, 2, y), [Vector3(0, 0, 1), Vector3(0, -2, 0)], Vector3.RIGHT) + generate_plane(array, Vector3(x, 2, y+1), [Vector3(1, 0, 0), Vector3(0, -2, 0)], Vector3.BACK) + generate_plane(array, Vector3(x+1, 2, y+1), [Vector3(0, 0, -1), Vector3(0, -2, 0)], Vector3.LEFT) + generate_plane(array, Vector3(x+1, 2, y), [Vector3(-1, 0, 0), Vector3(0, -2, 0)], Vector3.FORWARD) func _ready() -> void: var starting_axis = randi_range(0, 1) @@ -142,5 +149,18 @@ func _ready() -> void: var grid: Array[Tile] = [] grid.resize(grid_width * grid_height) grid.fill(Tile.WALL) + + var surface_array = [] + surface_array.resize(Mesh.ARRAY_MAX) + + surface_array[Mesh.ARRAY_VERTEX] = PackedVector3Array() + surface_array[Mesh.ARRAY_INDEX] = PackedInt32Array() + surface_array[Mesh.ARRAY_NORMAL] = PackedVector3Array() + generate_grid(map, grid) - generate_geo(grid) + generate_geo(grid, surface_array) + + mesh.mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, surface_array) + var tri_mesh = mesh.mesh.create_trimesh_shape() + $NavigationRegion3D/StaticBody3D/CollisionShape3D.shape = tri_mesh + $NavigationRegion3D.bake_navigation_mesh() diff --git a/scripts/enemy.gd b/scripts/enemy.gd index 6be60c7..1c4c063 100644 --- a/scripts/enemy.gd +++ b/scripts/enemy.gd @@ -62,6 +62,8 @@ func chase_nav_mesh(delta: float): var next_location = nav_agent.get_next_path_position() var new_velocity = (next_location - current_location).normalized() * speed + #print("Target is ", next_location) + # We only want to navigate on XZ plane new_velocity.y = 0 -- cgit