summaryrefslogtreecommitdiff
path: root/scene/3d/navigation_obstacle_3d.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/3d/navigation_obstacle_3d.cpp')
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp92
1 files changed, 76 insertions, 16 deletions
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 308545b2cc..9b49238333 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -35,6 +35,11 @@
#include "servers/navigation_server_3d.h"
void NavigationObstacle3D::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_rid"), &NavigationObstacle3D::get_rid);
+
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationObstacle3D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationObstacle3D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle3D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle3D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle3D::set_radius);
@@ -54,32 +59,50 @@ void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE: {
- parent_node3d = Object::cast_to<Node3D>(get_parent());
- reevaluate_agent_radius();
- if (parent_node3d != nullptr) {
- // place agent on navigation map first or else the RVO agent callback creation fails silently later
- NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
- }
+ case NOTIFICATION_POST_ENTER_TREE: {
+ set_agent_parent(get_parent());
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
- parent_node3d = nullptr;
+ set_agent_parent(nullptr);
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
- parent_node3d = Object::cast_to<Node3D>(get_parent());
- reevaluate_agent_radius();
+ if (is_inside_tree() && (get_parent() != parent_node3d)) {
+ set_agent_parent(get_parent());
+ set_physics_process_internal(true);
+ }
} break;
case NOTIFICATION_UNPARENTED: {
- parent_node3d = nullptr;
+ set_agent_parent(nullptr);
+ set_physics_process_internal(false);
+ } break;
+
+ case NOTIFICATION_PAUSED: {
+ if (parent_node3d && !parent_node3d->can_process()) {
+ map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid());
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
+ } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) {
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ map_before_pause = RID();
+ }
+ } break;
+
+ case NOTIFICATION_UNPAUSED: {
+ if (parent_node3d && !parent_node3d->can_process()) {
+ map_before_pause = NavigationServer3D::get_singleton()->agent_get_map(get_rid());
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
+ } else if (parent_node3d && parent_node3d->can_process() && !(map_before_pause == RID())) {
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_before_pause);
+ map_before_pause = RID();
+ }
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- if (parent_node3d) {
+ if (parent_node3d && parent_node3d->is_inside_tree()) {
NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin);
PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent());
@@ -107,14 +130,19 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
if (!Object::cast_to<Node3D>(get_parent())) {
- warnings.push_back(TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object."));
+ warnings.push_back(RTR("The NavigationObstacle3D only serves to provide collision avoidance to a Node3D inheriting parent object."));
+ }
+
+ if (Object::cast_to<StaticBody3D>(get_parent())) {
+ warnings.push_back(RTR("The NavigationObstacle3D is intended for constantly moving bodies like CharacterBody3D or RigidBody3D as it creates only an RVO avoidance radius and does not follow scene geometry exactly."
+ "\nNot constantly moving or complete static objects should be (re)baked to a NavigationMesh so agents can not only avoid them but also move along those objects outline at high detail"));
}
return warnings;
}
void NavigationObstacle3D::initialize_agent() {
- NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
+ NavigationServer3D::get_singleton()->agent_set_neighbor_distance(agent, 0.0);
NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0);
NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0);
NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0);
@@ -129,13 +157,13 @@ void NavigationObstacle3D::reevaluate_agent_radius() {
}
real_t NavigationObstacle3D::estimate_agent_radius() const {
- if (parent_node3d) {
+ if (parent_node3d && parent_node3d->is_inside_tree()) {
// Estimate the radius of this physics body
real_t radius = 0.0;
for (int i(0); i < parent_node3d->get_child_count(); i++) {
// For each collision shape
CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i));
- if (cs) {
+ if (cs && cs->is_inside_tree()) {
// Take the distance between the Body center to the shape center
real_t r = cs->get_transform().origin.length();
if (cs->get_shape().is_valid()) {
@@ -146,6 +174,9 @@ real_t NavigationObstacle3D::estimate_agent_radius() const {
r *= MAX(s.x, MAX(s.y, s.z));
// Takes the biggest radius
radius = MAX(radius, r);
+ } else if (cs && !cs->is_inside_tree()) {
+ WARN_PRINT("A CollisionShape3D of the NavigationObstacle3D parent node was not inside the SceneTree when estimating the obstacle radius."
+ "\nMove the NavigationObstacle3D to a child position below any CollisionShape3D node of the parent node so the CollisionShape3D is already inside the SceneTree.");
}
}
@@ -159,6 +190,35 @@ real_t NavigationObstacle3D::estimate_agent_radius() const {
return 1.0; // Never a 0 radius
}
+void NavigationObstacle3D::set_agent_parent(Node *p_agent_parent) {
+ if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) {
+ parent_node3d = Object::cast_to<Node3D>(p_agent_parent);
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), map_override);
+ } else {
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), parent_node3d->get_world_3d()->get_navigation_map());
+ }
+ reevaluate_agent_radius();
+ } else {
+ parent_node3d = nullptr;
+ NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID());
+ }
+}
+
+void NavigationObstacle3D::set_navigation_map(RID p_navigation_map) {
+ map_override = p_navigation_map;
+ NavigationServer3D::get_singleton()->agent_set_map(agent, map_override);
+}
+
+RID NavigationObstacle3D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (parent_node3d != nullptr) {
+ return parent_node3d->get_world_3d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) {
estimate_radius = p_estimate_radius;
notify_property_list_changed();