feat: units use navigation obstacle avoidance

This commit is contained in:
Sara 2024-08-04 16:27:46 +02:00
parent f883d7d67e
commit 0e8bcf4442
14 changed files with 196 additions and 129 deletions

View file

@ -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="."]

View file

@ -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)

View file

@ -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)

View file

@ -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<Unit>(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<Unit>(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<Unit>(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);
}

View file

@ -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);

View file

@ -1,10 +1,10 @@
#include "planner.hpp"
#include "action_db.hpp"
#include "godot_cpp/templates/hashfuncs.hpp"
#include "utils/godot_macros.hpp"
#include <cstdint>
#include <godot_cpp/classes/engine.hpp>
#include <godot_cpp/classes/global_constants.hpp>
#include <godot_cpp/templates/hashfuncs.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
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>("../ActorWorldState");
void Planner::_ready() { GDGAMEONLY();
this->world_state = this->get_node<ActorWorldState>("%ActorWorldState");
}
Plan Planner::plan_for_goal(gd::Ref<Goal> goal) {

View file

@ -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> goal);

View file

@ -21,11 +21,11 @@
#include <godot_cpp/core/defs.hpp>
#include <godot_cpp/godot.hpp>
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<TankSelfHeal>();
goap::ActionDB::register_action<TakeCover>();
ClassDB::register_class<goap::ActorWorldState>();
ClassDB::register_class<goap::Goal>();
ClassDB::register_class<goap::Planner>();
ClassDB::register_class<goap::State>();
gd::ClassDB::register_class<goap::ActorWorldState>();
gd::ClassDB::register_class<goap::Goal>();
gd::ClassDB::register_class<goap::Planner>();
gd::ClassDB::register_class<goap::State>();
ClassDB::register_class<MoveTo>();
ClassDB::register_class<Animate>();
ClassDB::register_class<Activate>();
gd::ClassDB::register_class<MoveTo>();
gd::ClassDB::register_class<Animate>();
gd::ClassDB::register_class<Activate>();
ClassDB::register_class<UnitWorldState>();
ClassDB::register_class<EnemyWorldState>();
ClassDB::register_class<GoalMarker>();
ClassDB::register_class<Unit>();
ClassDB::register_class<RTSGameMode>();
ClassDB::register_class<RTSPlayer>();
ClassDB::register_class<EntityHealth>();
ClassDB::register_class<NavMarker>();
ClassDB::register_class<NavRoom>();
gd::ClassDB::register_class<UnitWorldState>();
gd::ClassDB::register_class<EnemyWorldState>();
gd::ClassDB::register_class<GoalMarker>();
gd::ClassDB::register_class<Unit>();
gd::ClassDB::register_class<RTSGameMode>();
gd::ClassDB::register_class<RTSPlayer>();
gd::ClassDB::register_class<EntityHealth>();
gd::ClassDB::register_class<NavMarker>();
gd::ClassDB::register_class<NavRoom>();
}
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();
}

View file

@ -7,37 +7,39 @@
void MoveTo::_bind_methods() {}
void MoveTo::_ready() {
this->parent_node3d = Object::cast_to<gd::Node3D>(this->get_parent());
this->parent_unit = Object::cast_to<Unit>(this->get_parent());
this->agent = this->get_node<gd::NavigationAgent3D>("../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 {

View file

@ -1,6 +1,7 @@
#ifndef RTS_STATES_HPP
#define RTS_STATES_HPP
#include "unit.hpp"
#include "goap/state.hpp"
#include <godot_cpp/classes/animation_player.hpp>
#include <godot_cpp/classes/navigation_agent3d.hpp>
@ -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};

View file

@ -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 <godot_cpp/classes/navigation_agent3d.hpp>
#include <godot_cpp/variant/callable_method_pointer.hpp>
@ -18,6 +19,7 @@ void Unit::_bind_methods() {
void Unit::_enter_tree() { GDGAMEONLY();
this->agent = this->get_node<gd::NavigationAgent3D>("%NavigationAgent3D");
this->agent->connect("velocity_computed", callable_mp(this, &Unit::on_velocity_computed));
this->planner = this->get_node<goap::Planner>("%Planner");
this->world_state = this->get_node<UnitWorldState>("%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<goap::Goal> 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<MoveTo>(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<goap::Goal> 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();
}
}
}

View file

@ -44,11 +44,12 @@ public:
bool has_plan() const;
EntityHealth *get_entity_health();
private:
void set_goal_and_plan(gd::Ref<goap::Goal> 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<goap::Goal> goal);
private:
gd::Callable on_state_finished{callable_mp(this, &Unit::state_finished)};
gd::Callable on_plan_failed{callable_mp(this, &Unit::replan_goal)};

View file

@ -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<Unit>(this->get_parent());
gd::UtilityFunctions::print("!!! ", this->get_path(), ": is part of ", this->parent_unit->get_path());
this->agent = this->get_node<gd::NavigationAgent3D>("%NavigationAgent3D");
this->eye_location = this->get_node<gd::Node3D>("%Eyes");
this->health = this->get_node<EntityHealth>("%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>("EntityHealth")})
if(EntityHealth *health{this->target_node->get_node<EntityHealth>("%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<Unit>(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();

View file

@ -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;