From 0e8bcf4442c1e2c04614161ba87c5c994467de9d Mon Sep 17 00:00:00 2001 From: Sara Date: Sun, 4 Aug 2024 16:27:46 +0200 Subject: [PATCH] feat: units use navigation obstacle avoidance --- godot/GameObjects/enemy_unit.tscn | 14 ++++- godot/GameObjects/player_unit.tscn | 15 +++-- godot/Levels/test_level.tscn | 95 +++++++++++++++--------------- src/enemy_world_state.cpp | 63 ++++++++++++-------- src/enemy_world_state.hpp | 2 + src/goap/planner.cpp | 6 +- src/goap/planner.hpp | 2 +- src/register_types.cpp | 42 ++++++------- src/rts_states.cpp | 30 ++++++---- src/rts_states.hpp | 6 +- src/unit.cpp | 33 +++++++---- src/unit.hpp | 3 +- src/unit_world_state.cpp | 12 +++- src/unit_world_state.hpp | 2 + 14 files changed, 196 insertions(+), 129 deletions(-) diff --git a/godot/GameObjects/enemy_unit.tscn b/godot/GameObjects/enemy_unit.tscn index a3b656b..fb6f662 100644 --- a/godot/GameObjects/enemy_unit.tscn +++ b/godot/GameObjects/enemy_unit.tscn @@ -4,12 +4,15 @@ [ext_resource type="AnimationLibrary" uid="uid://crkh5gahl2ci6" path="res://Animation/bean_characters.res" id="2_lrpu6"] [sub_resource type="Goal" id="Goal_1c3rq"] +requirements_dict = { +"has_target": false +} desired_state_dict = { "is_health_safe": true } [sub_resource type="SphereShape3D" id="SphereShape3D_5pqvg"] -radius = 8.42531 +radius = 15.6018 [sub_resource type="SphereShape3D" id="SphereShape3D_drlm2"] radius = 1.0 @@ -25,7 +28,7 @@ rings = 3 [sub_resource type="BoxMesh" id="BoxMesh_p8wvo"] size = Vector3(0.2, 0.2, 0.5) -[node name="Unit" type="Unit"] +[node name="Tank" type="Unit"] configure_team = 3 collision_layer = 6 collision_mask = 0 @@ -48,6 +51,10 @@ unique_name_in_owner = true path_desired_distance = 0.5 target_desired_distance = 0.2 path_height_offset = 0.5 +avoidance_enabled = true +radius = 0.75 +time_horizon_agents = 4.0 +time_horizon_obstacles = 4.0 [node name="AwarenessArea" type="Area3D" parent="."] unique_name_in_owner = true @@ -55,9 +62,10 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.1216, 3.55442) collision_layer = 0 collision_mask = 2 input_ray_pickable = false +monitorable = false [node name="CollisionShape3D" type="CollisionShape3D" parent="AwarenessArea"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.238092) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.735143) shape = SubResource("SphereShape3D_5pqvg") [node name="AnimationPlayer" type="AnimationPlayer" parent="."] diff --git a/godot/GameObjects/player_unit.tscn b/godot/GameObjects/player_unit.tscn index f08225f..849cf56 100644 --- a/godot/GameObjects/player_unit.tscn +++ b/godot/GameObjects/player_unit.tscn @@ -2,11 +2,13 @@ [ext_resource type="AnimationLibrary" uid="uid://crkh5gahl2ci6" path="res://Animation/bean_characters.res" id="1_70627"] -[sub_resource type="SphereShape3D" id="SphereShape3D_drlm2"] -radius = 1.0 +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_q7mxo"] +radius = 0.586696 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_n4q15"] +specular_mode = 1 albedo_color = Color(0.407843, 1, 0.447059, 1) +roughness = 0.0 [sub_resource type="CapsuleMesh" id="CapsuleMesh_5r0b3"] material = SubResource("StandardMaterial3D_n4q15") @@ -16,7 +18,7 @@ rings = 3 [sub_resource type="BoxMesh" id="BoxMesh_p8wvo"] size = Vector3(0.2, 0.2, 0.5) -[node name="Unit" type="Unit"] +[node name="Player" type="Unit"] configure_team = 1 collision_layer = 6 collision_mask = 0 @@ -36,7 +38,12 @@ unique_name_in_owner = true path_desired_distance = 0.2 target_desired_distance = 0.5 path_height_offset = 0.5 +avoidance_enabled = true +radius = 0.75 +time_horizon_agents = 4.0 +time_horizon_obstacles = 4.0 debug_enabled = true +debug_path_custom_point_size = 7.0 [node name="AnimationPlayer" type="AnimationPlayer" parent="."] unique_name_in_owner = true @@ -53,7 +60,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.39151, 0) [node name="CollisionShape3D" type="CollisionShape3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) -shape = SubResource("SphereShape3D_drlm2") +shape = SubResource("CapsuleShape3D_q7mxo") [node name="MeshInstance3D" type="MeshInstance3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) diff --git a/godot/Levels/test_level.tscn b/godot/Levels/test_level.tscn index 3294fbf..e74b7e2 100644 --- a/godot/Levels/test_level.tscn +++ b/godot/Levels/test_level.tscn @@ -151,7 +151,7 @@ mesh = SubResource("PlaneMesh_hohcb") skeleton = NodePath("../../../..") [node name="Floor2" type="StaticBody3D" parent="WorldEnvironment/NavigationRegion3D"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.901, 0, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.9976, 0, 0) collision_layer = 5 [node name="CollisionShape3D" type="CollisionShape3D" parent="WorldEnvironment/NavigationRegion3D/Floor2"] @@ -219,139 +219,139 @@ transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -23.14 [node name="ModernTrain6" parent="WorldEnvironment/NavigationRegion3D" instance=ExtResource("6_favl6")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -16.443, 0.268182, 3.2886) -[node name="Node3D" type="Node3D" parent="WorldEnvironment"] +[node name="Tracks" type="Node3D" parent="WorldEnvironment"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 7.25984) -[node name="track2" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track2" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -8.66062, 1.8999e-07, 0) -[node name="track3" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track3" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -6.18288, 1.8999e-07, 0) -[node name="track4" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track4" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -3.69507, 1.8999e-07, 0) -[node name="track5" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track5" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -1.21011, 1.8999e-07, 0) -[node name="track6" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track6" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 1.27448, 1.8999e-07, 0) -[node name="track7" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track7" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 3.75945, 1.8999e-07, 0) -[node name="track8" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track8" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 6.24283, 1.8999e-07, 0) -[node name="track9" parent="WorldEnvironment/Node3D" instance=ExtResource("7_8fuqb")] +[node name="track9" parent="WorldEnvironment/Tracks" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.7278, 1.8999e-07, 0) -[node name="Node3D2" type="Node3D" parent="WorldEnvironment"] +[node name="Tracks2" type="Node3D" parent="WorldEnvironment"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 3.17094) -[node name="track2" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track2" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -8.66062, 1.8999e-07, 0) -[node name="track3" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track3" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -6.18288, 1.8999e-07, 0) -[node name="track4" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track4" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -3.69507, 1.8999e-07, 0) -[node name="track5" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track5" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -1.21011, 1.8999e-07, 0) -[node name="track6" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track6" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 1.27448, 1.8999e-07, 0) -[node name="track7" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track7" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 3.75945, 1.8999e-07, 0) -[node name="track8" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track8" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 6.24283, 1.8999e-07, 0) -[node name="track9" parent="WorldEnvironment/Node3D2" instance=ExtResource("7_8fuqb")] +[node name="track9" parent="WorldEnvironment/Tracks2" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.7278, 1.8999e-07, 0) -[node name="Node3D3" type="Node3D" parent="WorldEnvironment"] +[node name="Tracks3" type="Node3D" parent="WorldEnvironment"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -4.67443) -[node name="track2" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track2" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -8.66062, 1.8999e-07, 0) -[node name="track3" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track3" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -6.18288, 1.8999e-07, 0) -[node name="track4" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track4" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -3.69507, 1.8999e-07, 0) -[node name="track5" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track5" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -1.21011, 1.8999e-07, 0) -[node name="track6" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track6" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 1.27448, 1.8999e-07, 0) -[node name="track7" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track7" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 3.75945, 1.8999e-07, 0) -[node name="track8" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track8" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 6.24283, 1.8999e-07, 0) -[node name="track9" parent="WorldEnvironment/Node3D3" instance=ExtResource("7_8fuqb")] +[node name="track9" parent="WorldEnvironment/Tracks3" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.7278, 1.8999e-07, 0) -[node name="Node3D4" type="Node3D" parent="WorldEnvironment"] +[node name="Tracks4" type="Node3D" parent="WorldEnvironment"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.8802, 0, 7.25984) -[node name="track2" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track2" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -8.66062, 1.8999e-07, 0) -[node name="track3" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track3" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -6.18288, 1.8999e-07, 0) -[node name="track4" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track4" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -3.69507, 1.8999e-07, 0) -[node name="track5" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track5" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -1.21011, 1.8999e-07, 0) -[node name="track6" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track6" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 1.27448, 1.8999e-07, 0) -[node name="track7" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track7" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 3.75945, 1.8999e-07, 0) -[node name="track8" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track8" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 6.24283, 1.8999e-07, 0) -[node name="track9" parent="WorldEnvironment/Node3D4" instance=ExtResource("7_8fuqb")] +[node name="track9" parent="WorldEnvironment/Tracks4" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.7278, 1.8999e-07, 0) -[node name="Node3D5" type="Node3D" parent="WorldEnvironment"] +[node name="Tracks5" type="Node3D" parent="WorldEnvironment"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -19.8802, 0, 3.17094) -[node name="track2" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track2" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -8.66062, 1.8999e-07, 0) -[node name="track3" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track3" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -6.18288, 1.8999e-07, 0) -[node name="track4" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track4" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -3.69507, 1.8999e-07, 0) -[node name="track5" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track5" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -1.21011, 1.8999e-07, 0) -[node name="track6" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track6" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 1.27448, 1.8999e-07, 0) -[node name="track7" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track7" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 3.75945, 1.8999e-07, 0) -[node name="track8" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track8" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 6.24283, 1.8999e-07, 0) -[node name="track9" parent="WorldEnvironment/Node3D5" instance=ExtResource("7_8fuqb")] +[node name="track9" parent="WorldEnvironment/Tracks5" instance=ExtResource("7_8fuqb")] transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 8.7278, 1.8999e-07, 0) [node name="Player" parent="." instance=ExtResource("3_wl7wm")] @@ -360,9 +360,6 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7.16025, 1.8999e-07, -2.07711 [node name="Player2" parent="." instance=ExtResource("3_wl7wm")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.28022, 1.8999e-07, -4.279) -[node name="Tank" parent="." instance=ExtResource("4_0o33v")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6.70922, -7.63685e-07, 3.72251) - [node name="Tank2" parent="." instance=ExtResource("4_0o33v")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.4143, -7.63685e-07, 5.91707) diff --git a/src/enemy_world_state.cpp b/src/enemy_world_state.cpp index a91b78e..01b5ec9 100644 --- a/src/enemy_world_state.cpp +++ b/src/enemy_world_state.cpp @@ -1,5 +1,6 @@ #include "enemy_world_state.hpp" #include "entity_health.hpp" +#include "godot_cpp/variant/utility_functions.hpp" #include "unit.hpp" #include "goap/goal.hpp" #include "utils/godot_macros.hpp" @@ -23,38 +24,19 @@ void EnemyWorldState::_ready() { GDGAMEONLY(); } void EnemyWorldState::on_awareness_entered(gd::Node3D *node) { - Unit *unit{gd::Object::cast_to(node)}; - if(unit == nullptr) - return; - if(unit == this->parent_unit) - return; - if(unit->get_entity_health()->get_is_dead()) - return; - unit->get_entity_health()->connect("death", this->aware_unit_death.bind(unit)); - this->known_enemies.push_back(unit); - this->try_set_target(this->select_target_from_known()); + if(Unit *unit{gd::Object::cast_to(node)}) { + gd::UtilityFunctions::print("!!! ", this->get_path(), ": ", node->get_path(), " entered awareness"); + this->add_aware_unit(unit); + } } void EnemyWorldState::on_awareness_exited(gd::Node3D *node) { Unit *unit{gd::Object::cast_to(node)}; - if(unit == nullptr) - return; - if(this->known_enemies.has(unit)) { - unit->get_entity_health()->disconnect("death", this->aware_unit_death.bind(unit)); - this->known_enemies.erase(unit); - } - if(unit == this->target_node) { - this->select_and_set_target(); - } + this->remove_aware_unit(unit); } void EnemyWorldState::on_damaged(int remaining, int delta, Unit *source) { - if(!this->known_enemies.has(source)) { - source->get_entity_health()->connect("death", this->aware_unit_death.bind(source)); - this->known_enemies.push_back(source); - } - if(!this->parent_unit->has_plan()) - this->try_set_target(this->select_target_from_known()); + this->add_aware_unit(source); } Unit *EnemyWorldState::select_target_from_known_with_priority(float *out_priority) { @@ -75,6 +57,37 @@ Unit *EnemyWorldState::select_target_from_known() { return this->select_target_from_known_with_priority(&dummy); } +void EnemyWorldState::add_aware_unit(Unit *unit) { + if(unit == nullptr) + return; + if(unit == this->parent_unit) + return; + if(this->known_enemies.has(unit)) + return; + if(unit->get_entity_health()->get_is_dead()) + return; + if(!this->get_is_unit_enemy(unit)) + return; + unit->get_entity_health()->connect("death", this->aware_unit_death.bind(unit)); + this->known_enemies.insert(0, unit); + if(!this->parent_unit->has_plan()) + this->try_set_target(this->select_target_from_known()); + else + gd::UtilityFunctions::print("!!! ", this->get_path(), " not replanning because a plan is already in motion"); + gd::UtilityFunctions::print("!!! ", this->get_path(), " found ", unit->get_path()); +} + +void EnemyWorldState::remove_aware_unit(Unit *unit) { + if(unit == nullptr) + return; + if(!this->known_enemies.has(unit)) + return; + unit->get_entity_health()->disconnect("death", this->aware_unit_death.bind(unit)); + this->known_enemies.erase(unit); + if(unit == this->target_node) + this->select_and_set_target(); +} + void EnemyWorldState::on_aware_unit_death(Unit *_, Unit *dead_entity) { this->on_awareness_exited(dead_entity); } diff --git a/src/enemy_world_state.hpp b/src/enemy_world_state.hpp index 9105bd7..ea8cb3b 100644 --- a/src/enemy_world_state.hpp +++ b/src/enemy_world_state.hpp @@ -28,6 +28,8 @@ public: //! Shorthand for select_target_from_known_with_priority(nullptr) Unit *select_target_from_known(); private: + void add_aware_unit(Unit *unit); + void remove_aware_unit(Unit *unit); void select_and_set_target(); void on_aware_unit_death(Unit *_, Unit *dead_entity); float calculate_priority(Unit *target); diff --git a/src/goap/planner.cpp b/src/goap/planner.cpp index 5e25565..2f16f99 100644 --- a/src/goap/planner.cpp +++ b/src/goap/planner.cpp @@ -1,10 +1,10 @@ #include "planner.hpp" #include "action_db.hpp" -#include "godot_cpp/templates/hashfuncs.hpp" #include "utils/godot_macros.hpp" #include #include #include +#include #include namespace goap { @@ -85,8 +85,8 @@ void Planner::_bind_methods() { GDPROPERTY_HINTED(actions_inspector, gd::Variant::ARRAY, gd::PROPERTY_HINT_ARRAY_TYPE, ActionDB::get_array_hint()); } -void Planner::_enter_tree() { GDGAMEONLY(); - this->world_state = this->get_node("../ActorWorldState"); +void Planner::_ready() { GDGAMEONLY(); + this->world_state = this->get_node("%ActorWorldState"); } Plan Planner::plan_for_goal(gd::Ref goal) { diff --git a/src/goap/planner.hpp b/src/goap/planner.hpp index 253d615..470a1cc 100644 --- a/src/goap/planner.hpp +++ b/src/goap/planner.hpp @@ -53,7 +53,7 @@ class Planner : public gd::Node { GDCLASS(Planner, gd::Node); static void _bind_methods(); public: - virtual void _enter_tree() override; + virtual void _ready() override; //! Compute a plan to achieve the passed `Goal`. Plan plan_for_goal(gd::Ref goal); diff --git a/src/register_types.cpp b/src/register_types.cpp index 6060985..dbc9350 100644 --- a/src/register_types.cpp +++ b/src/register_types.cpp @@ -21,11 +21,11 @@ #include #include -using namespace godot; +namespace gd = godot; -void initialize_gdextension_types(ModuleInitializationLevel p_level) +void initialize_gdextension_types(gd::ModuleInitializationLevel p_level) { - if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + if (p_level != gd::MODULE_INITIALIZATION_LEVEL_SCENE) { return; } utils::godot_cpp_utils_register_types(); @@ -40,24 +40,24 @@ void initialize_gdextension_types(ModuleInitializationLevel p_level) goap::ActionDB::register_action(); goap::ActionDB::register_action(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); + gd::ClassDB::register_class(); } extern "C" @@ -65,9 +65,9 @@ extern "C" // Initialization GDExtensionBool GDE_EXPORT metro_rts_library_init(GDExtensionInterfaceGetProcAddress p_get_proc_address, GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization) { - GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); + gd::GDExtensionBinding::InitObject init_obj(p_get_proc_address, p_library, r_initialization); init_obj.register_initializer(initialize_gdextension_types); - init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE); + init_obj.set_minimum_library_initialization_level(gd::MODULE_INITIALIZATION_LEVEL_SCENE); return init_obj.init(); } diff --git a/src/rts_states.cpp b/src/rts_states.cpp index 25f39aa..011037d 100644 --- a/src/rts_states.cpp +++ b/src/rts_states.cpp @@ -7,37 +7,39 @@ void MoveTo::_bind_methods() {} void MoveTo::_ready() { - this->parent_node3d = Object::cast_to(this->get_parent()); + this->parent_unit = Object::cast_to(this->get_parent()); this->agent = this->get_node("../NavigationAgent3D"); if(this->target_node == nullptr) { this->state_ended(); gd::UtilityFunctions::push_warning("failed to start MoveTo state due to missing target"); return; } - this->agent->set_target_position(this->target_node->get_global_position()); + this->agent->connect("velocity_computed", this->nav_velocity_computed); this->calculate_path(); - gd::UtilityFunctions::print(this->parent_node3d->get_path(), " MoveTo ", this->target_node->get_path()); + gd::UtilityFunctions::print(this->parent_unit->get_path(), " MoveTo ", this->target_node->get_path()); } void MoveTo::_end_state() { - this->agent->set_target_position(this->parent_node3d->get_global_position()); + this->agent->disconnect("velocity_computed", this->nav_velocity_computed); + this->agent->set_target_position(this->parent_unit->get_global_position()); } void MoveTo::_process(double delta_time) { - gd::Vector3 const pos = this->parent_node3d->get_global_position(); + gd::Vector3 const pos = this->parent_unit->get_global_position(); gd::Vector3 const target = this->agent->get_next_path_position(); gd::Vector3 const direction = (target - pos).normalized(); - this->parent_node3d->set_global_position(pos + direction * delta_time); - this->parent_node3d->look_at(pos - gd::Vector3{direction.x, 0.f, direction.z}); - + this->agent->set_velocity(direction); bool const navigation_finished{this->agent->is_navigation_finished()}; if(this->is_action_done_interrupt() || navigation_finished) this->state_ended(); - if((utils::time_seconds() - this->last_repath) > this->get_repath_interval()) this->calculate_path(); } +void MoveTo::_physics_process(double delta) { + this->parent_unit->move_and_slide(); +} + void MoveTo::calculate_path() { gd::Vector3 const target_pos{this->target_node->get_global_position()}; this->agent->set_target_position(target_pos); @@ -45,6 +47,14 @@ void MoveTo::calculate_path() { this->target_position_at_last = target_pos; } +void MoveTo::on_velocity_computed(gd::Vector3 vel) { + gd::Vector3 const pos{this->parent_unit->get_global_position()}; + gd::Vector3 const target_dir{(this->agent->get_next_path_position() - pos).normalized()}; + // recalculate if avoidance is causing the motion to lock up + if(target_dir.dot(vel.normalized()) < 0.25f) + this->calculate_path(); +} + float MoveTo::target_delta_position() const { return this->target_position_at_last .distance_to(this->target_node->get_global_position()); @@ -52,7 +62,7 @@ float MoveTo::target_delta_position() const { float MoveTo::distance_to_target() const { return this->target_position_at_last - .distance_to(this->parent_node3d->get_global_position()); + .distance_to(this->parent_unit->get_global_position()); } double MoveTo::get_repath_interval() const { diff --git a/src/rts_states.hpp b/src/rts_states.hpp index de1f7f0..ce032ac 100644 --- a/src/rts_states.hpp +++ b/src/rts_states.hpp @@ -1,6 +1,7 @@ #ifndef RTS_STATES_HPP #define RTS_STATES_HPP +#include "unit.hpp" #include "goap/state.hpp" #include #include @@ -14,14 +15,17 @@ public: virtual void _ready() override; virtual void _end_state() override; virtual void _process(double delta_time) override; + virtual void _physics_process(double delta_time) override; void calculate_path(); + void on_velocity_computed(gd::Vector3 vel); float target_delta_position() const; float distance_to_target() const; double get_repath_interval() const; public: gd::Node3D *target_node{nullptr}; private: - gd::Node3D *parent_node3d{nullptr}; + gd::Callable nav_velocity_computed{callable_mp(this, &MoveTo::on_velocity_computed)}; + Unit *parent_unit{nullptr}; gd::NavigationAgent3D *agent{nullptr}; gd::Vector3 target_position_at_last{}; double last_repath{0.0}; diff --git a/src/unit.cpp b/src/unit.cpp index b81c5d1..cdc7dbe 100644 --- a/src/unit.cpp +++ b/src/unit.cpp @@ -2,6 +2,7 @@ #include "entity_health.hpp" #include "goap/action.hpp" #include "goap/goal.hpp" +#include "rts_states.hpp" #include "utils/godot_macros.hpp" #include #include @@ -18,6 +19,7 @@ void Unit::_bind_methods() { void Unit::_enter_tree() { GDGAMEONLY(); this->agent = this->get_node("%NavigationAgent3D"); + this->agent->connect("velocity_computed", callable_mp(this, &Unit::on_velocity_computed)); this->planner = this->get_node("%Planner"); this->world_state = this->get_node("%ActorWorldState"); this->world_state->connect("attention_changed", callable_mp(this, &Unit::stop_plan)); @@ -111,16 +113,15 @@ EntityHealth *Unit::get_entity_health() { return this->health; } -void Unit::set_goal_and_plan(gd::Ref goal) { - this->current_goal = goal; - if(goal.is_null()) { - this->current_plan.clear(); - } else { - this->current_plan = this->planner->plan_for_goal(goal); - if(this->current_plan.is_empty()) { - this->current_goal.unref(); - } - } +void Unit::on_velocity_computed(gd::Vector3 vel) { + gd::Vector3 const pos{this->get_global_position()}; + gd::Vector3 const target_dir{(this->agent->get_next_path_position() - pos).normalized()}; + if(vel.x == 0 && vel.z == 0) + return; + if(gd::Object::cast_to(this->state) == nullptr) + return; + this->set_velocity(vel); + this->look_at(pos - gd::Vector3{vel.x, 0.f, vel.z}); } void Unit::destroy_state() { @@ -166,3 +167,15 @@ void Unit::next_action() { void Unit::replan_goal() { this->begin_goal(this->current_goal); } + +void Unit::set_goal_and_plan(gd::Ref goal) { + this->current_goal = goal; + if(goal.is_null()) { + this->current_plan.clear(); + } else { + this->current_plan = this->planner->plan_for_goal(goal); + if(this->current_plan.is_empty()) { + this->current_goal.unref(); + } + } +} diff --git a/src/unit.hpp b/src/unit.hpp index 8d6eaa3..6ebf318 100644 --- a/src/unit.hpp +++ b/src/unit.hpp @@ -44,11 +44,12 @@ public: bool has_plan() const; EntityHealth *get_entity_health(); private: - void set_goal_and_plan(gd::Ref goal); + void on_velocity_computed(gd::Vector3 vel); void destroy_state(); void state_finished(); void next_action(); void replan_goal(); + void set_goal_and_plan(gd::Ref goal); private: gd::Callable on_state_finished{callable_mp(this, &Unit::state_finished)}; gd::Callable on_plan_failed{callable_mp(this, &Unit::replan_goal)}; diff --git a/src/unit_world_state.cpp b/src/unit_world_state.cpp index 8f1fea1..3d4997d 100644 --- a/src/unit_world_state.cpp +++ b/src/unit_world_state.cpp @@ -14,6 +14,7 @@ void UnitWorldState::_bind_methods() { GDFUNCTION(get_can_see_target); GDFUNCTION(get_is_target_dead); GDFUNCTION(get_is_at_target); + GDFUNCTION(get_has_target); GDFUNCTION(get_target_node); GDFUNCTION(get_is_target_enemy); GDFUNCTION(get_is_in_melee_range); @@ -24,6 +25,7 @@ void UnitWorldState::_bind_methods() { void UnitWorldState::_enter_tree() { GDGAMEONLY(); this->parent_unit = gd::Object::cast_to(this->get_parent()); + gd::UtilityFunctions::print("!!! ", this->get_path(), ": is part of ", this->parent_unit->get_path()); this->agent = this->get_node("%NavigationAgent3D"); this->eye_location = this->get_node("%Eyes"); this->health = this->get_node("%EntityHealth"); @@ -61,8 +63,12 @@ bool UnitWorldState::get_is_at_target() const { return (target - current).length_squared() <= min_dist * min_dist; } +bool UnitWorldState::get_has_target() const { + return this->target_node != nullptr; +} + bool UnitWorldState::get_is_target_dead() const { - if(EntityHealth *health{this->target_node->get_node("EntityHealth")}) + if(EntityHealth *health{this->target_node->get_node("%EntityHealth")}) return health->get_injury_current() <= 0.f; return false; } @@ -73,6 +79,10 @@ bool UnitWorldState::get_is_target_unit() const { bool UnitWorldState::get_is_target_enemy() const { Unit *unit{gd::Object::cast_to(this->target_node)}; + return unit != nullptr && this->get_is_unit_enemy(unit); +} + +bool UnitWorldState::get_is_unit_enemy(Unit *unit) const { return unit != nullptr && unit->get_team() != UnitTeam::Neutral && unit->get_team() != this->parent_unit->get_team(); diff --git a/src/unit_world_state.hpp b/src/unit_world_state.hpp index a0824ed..a24d104 100644 --- a/src/unit_world_state.hpp +++ b/src/unit_world_state.hpp @@ -15,9 +15,11 @@ public: virtual void _enter_tree() override; bool get_can_see_target() const; bool get_is_at_target() const; + bool get_has_target() const; bool get_is_target_dead() const; bool get_is_target_unit() const; bool get_is_target_enemy() const; + bool get_is_unit_enemy(Unit *unit) const; bool get_is_in_melee_range() const; bool get_is_health_safe() const; gd::Vector3 get_parent_global_position() const;