diff --git a/3d/navigation/character.gd b/3d/navigation/character.gd new file mode 100644 index 00000000..bc00fdd1 --- /dev/null +++ b/3d/navigation/character.gd @@ -0,0 +1,45 @@ +extends Marker3D + + +const Line3D = preload("res://line3d.gd") + +@export var character_speed := 10.0 +@export var show_path := true + +@onready var _nav_agent := $NavigationAgent3D as NavigationAgent3D + +var _nav_path_line : Line3D + + +func _ready(): + _nav_path_line = Line3D.new() + add_child(_nav_path_line) + _nav_path_line.set_as_top_level(true) + + +func _physics_process(delta): + if _nav_agent.is_navigation_finished(): + return + var next_position := _nav_agent.get_next_path_position() + var offset := next_position - global_position + global_position = global_position.move_toward(next_position, delta * character_speed) + + # Make the robot look at the direction we're traveling. + # Clamp y to 0 so the robot only looks left and right, not up/down. + offset.y = 0 + look_at(global_position + offset, Vector3.UP) + + +func set_target_position(target_position: Vector3): + _nav_agent.set_target_position(target_position) + # Get a full navigation path with the NavigationServer API. + if show_path: + var start_position := global_transform.origin + var optimize := true + var navigation_map := get_world_3d().get_navigation_map() + var path := NavigationServer3D.map_get_path( + navigation_map, + start_position, + target_position, + optimize) + _nav_path_line.draw_path(path) diff --git a/3d/navigation/line3d.gd b/3d/navigation/line3d.gd new file mode 100644 index 00000000..eb6193d4 --- /dev/null +++ b/3d/navigation/line3d.gd @@ -0,0 +1,22 @@ +extends MeshInstance3D + + +func _ready(): + set_mesh(ImmediateMesh.new()) + var material := StandardMaterial3D.new() + material.flags_unshaded = true + material.albedo_color = Color.WHITE + set_material_override(material) + + +func draw_path(path): + var im: ImmediateMesh = mesh + im.clear_surfaces() + im.surface_begin(Mesh.PRIMITIVE_POINTS, null) + im.surface_add_vertex(path[0]) + im.surface_add_vertex(path[path.size() - 1]) + im.surface_end() + im.surface_begin(Mesh.PRIMITIVE_LINE_STRIP, null) + for current_vector in path: + im.surface_add_vertex(current_vector) + im.surface_end() diff --git a/3d/navigation/navmesh.gd b/3d/navigation/navmesh.gd index e722b094..437afe5b 100644 --- a/3d/navigation/navmesh.gd +++ b/3d/navigation/navmesh.gd @@ -1,84 +1,30 @@ extends Node3D -const SPEED := 10.0 -@export var show_path := true +const Character = preload("res://character.gd") -var cam_rotation := 0.0 -var path: PackedVector3Array +@onready var _camera := $CameraBase/Camera3D as Camera3D +@onready var _robot := $RobotBase as Character -@onready var robot: Marker3D = $RobotBase -@onready var camera: Camera3D = $CameraBase/Camera3D - -func _ready(): - set_process_input(true) - - -func _physics_process(delta: float): - var direction := Vector3() - - # We need to scale the movement speed by how much delta has passed, - # otherwise the motion won't be smooth. - var step_size := delta * SPEED - - if not path.is_empty(): - # Direction is the difference between where we are now - # and where we want to go. - var destination := path[0] - direction = destination - robot.position - - # If the next node is closer than we intend to 'step', then - # take a smaller step. Otherwise we would go past it and - # potentially go through a wall or over a cliff edge! - if step_size > direction.length(): - step_size = direction.length() - # We should also remove this node since we're about to reach it. - path.remove_at(0) - - # Move the robot towards the path node, by how far we want to travel. - # TODO: This information should be set to the CharacterBody properties instead of arguments. - # Note: For a CharacterBody3D, we would instead use move_and_slide - # so collisions work properly. - robot.position += direction.normalized() * step_size - - # Lastly let's make sure we're looking in the direction we're traveling. - # Clamp y to 0 so the robot only looks left and right, not up/down. - direction.y = 0 - if direction: - # Direction is relative, so apply it to the robot's location to - # get a point we can actually look at. - var look_at_point := robot.position + direction.normalized() - # Make the robot look at the point. - robot.look_at(look_at_point, Vector3.UP) +var _cam_rotation := 0.0 func _unhandled_input(event: InputEvent): if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed: - var map := get_world_3d().navigation_map - var from := camera.project_ray_origin(event.position) - var to := from + camera.project_ray_normal(event.position) * 1000 - var target_point := NavigationServer3D.map_get_closest_point_to_segment(map, from, to) + # Get closest point on navmesh for the current mouse cursor position. + var mouse_cursor_position = event.position + var camera_ray_length := 1000.0 + var camera_ray_start := _camera.project_ray_origin(mouse_cursor_position) + var camera_ray_end := camera_ray_start + _camera.project_ray_normal(mouse_cursor_position) * camera_ray_length - # Set the path between the robot's current location and our target. - path = NavigationServer3D.map_get_path(map, robot.position, target_point, true) - - if show_path: - draw_path(path) + var closest_point_on_navmesh := NavigationServer3D.map_get_closest_point_to_segment( + get_world_3d().navigation_map, + camera_ray_start, + camera_ray_end + ) + _robot.set_target_position(closest_point_on_navmesh) elif event is InputEventMouseMotion: if event.button_mask & (MOUSE_BUTTON_MASK_MIDDLE + MOUSE_BUTTON_MASK_RIGHT): - cam_rotation += event.relative.x * 0.005 - $CameraBase.set_rotation(Vector3(0, cam_rotation, 0)) - - -func draw_path(path_array: PackedVector3Array) -> void: - var im: ImmediateMesh = $DrawPath.mesh - im.clear_surfaces() - im.surface_begin(Mesh.PRIMITIVE_POINTS, null) - im.surface_add_vertex(path_array[0]) - im.surface_add_vertex(path_array[path_array.size() - 1]) - im.surface_end() - im.surface_begin(Mesh.PRIMITIVE_LINE_STRIP, null) - for current_vector in path: - im.surface_add_vertex(current_vector) - im.surface_end() + _cam_rotation += event.relative.x * 0.005 + $CameraBase.set_rotation(Vector3.UP * _cam_rotation) diff --git a/3d/navigation/navmesh.res b/3d/navigation/navmesh.res index a768b066..cafb9179 100644 Binary files a/3d/navigation/navmesh.res and b/3d/navigation/navmesh.res differ diff --git a/3d/navigation/navmesh.tscn b/3d/navigation/navmesh.tscn index bb6b1560..c590dd4c 100644 --- a/3d/navigation/navmesh.tscn +++ b/3d/navigation/navmesh.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=10 format=3 uid="uid://dkhg8e00d02f2"] +[gd_scene load_steps=9 format=3 uid="uid://dkhg8e00d02f2"] [ext_resource type="Script" path="res://navmesh.gd" id="1"] -[ext_resource type="NavigationMesh" uid="uid://xl4o2ckjxava" path="res://navmesh.res" id="2_lcfvj"] -[ext_resource type="ArrayMesh" uid="uid://gmytdjp2bcsq" path="res://level_mesh.res" id="3_1cas0"] -[ext_resource type="ArrayMesh" uid="uid://prwe6io8p1iv" path="res://robot.res" id="5_ple0n"] +[ext_resource type="NavigationMesh" uid="uid://ii7ckx3dvrs2" path="res://navmesh.res" id="2_lcfvj"] +[ext_resource type="ArrayMesh" path="res://level_mesh.res" id="3_1cas0"] +[ext_resource type="Script" path="res://character.gd" id="4_u7d3d"] +[ext_resource type="ArrayMesh" path="res://robot.res" id="5_ple0n"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_ps07l"] sky_horizon_color = Color(0.64625, 0.65575, 0.67075, 1) @@ -18,11 +19,6 @@ sky = SubResource("Sky_baxfs") tonemap_mode = 3 tonemap_white = 6.0 -[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_noou6"] -shading_mode = 0 - -[sub_resource type="ImmediateMesh" id="ImmediateMesh_dvj5w"] - [node name="Node3D" type="Node3D"] _import_path = NodePath(".") script = ExtResource("1") @@ -31,24 +27,19 @@ script = ExtResource("1") environment = SubResource("Environment_c0if4") [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] -transform = Transform3D(0.623013, -0.733525, 0.271653, 0.321394, 0.55667, 0.766045, -0.713134, -0.389948, 0.582563, 10.0773, 5.02381, 0) -light_energy = 5.0 +transform = Transform3D(-0.723713, -0.678436, -0.12635, -0.277002, 0.11789, 0.95361, -0.632068, 0.725139, -0.273247, 10.0773, 5.02381, 0) shadow_enabled = true directional_shadow_mode = 0 directional_shadow_fade_start = 1.0 directional_shadow_max_distance = 35.0 [node name="NavigationRegion3D" type="NavigationRegion3D" parent="."] -navmesh = ExtResource("2_lcfvj") +navigation_mesh = ExtResource("2_lcfvj") [node name="LevelMesh" type="MeshInstance3D" parent="NavigationRegion3D"] transform = Transform3D(2, 0, 0, 0, 2, 0, 0, 0, 2, 0, -0.0452547, 0) mesh = ExtResource("3_1cas0") -[node name="DrawPath" type="MeshInstance3D" parent="."] -material_override = SubResource("StandardMaterial3D_noou6") -mesh = SubResource("ImmediateMesh_dvj5w") - [node name="CameraBase" type="Node3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.07475, 0, 1.96678) @@ -58,8 +49,12 @@ fov = 50.0 near = 0.1 [node name="RobotBase" type="Marker3D" parent="."] +script = ExtResource("4_u7d3d") [node name="Robot" type="MeshInstance3D" parent="RobotBase"] transform = Transform3D(-0.5, 0, -7.54979e-08, 0, 1, 0, 7.54979e-08, 0, -0.5, 0, 1, 0) gi_mode = 2 mesh = ExtResource("5_ple0n") + +[node name="NavigationAgent3D" type="NavigationAgent3D" parent="RobotBase"] +path_desired_distance = 0.1 diff --git a/3d/navigation/particle.png b/3d/navigation/particle.png deleted file mode 100644 index 9d6a4e13..00000000 Binary files a/3d/navigation/particle.png and /dev/null differ diff --git a/3d/navigation/particle.png.import b/3d/navigation/particle.png.import deleted file mode 100644 index 449ac3d0..00000000 --- a/3d/navigation/particle.png.import +++ /dev/null @@ -1,36 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c27ndr5sgvyr8" -path.s3tc="res://.godot/imported/particle.png-c2ba3d91e96c62035d672392a1197218.s3tc.ctex" -path.etc2="res://.godot/imported/particle.png-c2ba3d91e96c62035d672392a1197218.etc2.ctex" -metadata={ -"imported_formats": ["s3tc", "etc2"], -"vram_texture": true -} - -[deps] - -source_file="res://particle.png" -dest_files=["res://.godot/imported/particle.png-c2ba3d91e96c62035d672392a1197218.s3tc.ctex", "res://.godot/imported/particle.png-c2ba3d91e96c62035d672392a1197218.etc2.ctex"] - -[params] - -compress/mode=2 -compress/lossy_quality=0.7 -compress/hdr_compression=1 -compress/bptc_ldr=0 -compress/normal_map=0 -compress/channel_pack=0 -mipmaps/generate=false -mipmaps/limit=-1 -roughness/mode=0 -roughness/src_normal="" -process/fix_alpha_border=true -process/premult_alpha=false -process/normal_map_invert_y=false -process/hdr_as_srgb=false -process/hdr_clamp_exposure=false -process/size_limit=0 -detect_3d/compress_to=1