diff options
Diffstat (limited to 'scene/3d/navigation_agent_3d.cpp')
-rw-r--r-- | scene/3d/navigation_agent_3d.cpp | 97 |
1 files changed, 78 insertions, 19 deletions
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 88f676d031..09d75dd284 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -65,8 +65,11 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent3D::set_path_max_distance); ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent3D::get_path_max_distance); - ClassDB::bind_method(D_METHOD("set_navigable_layers", "navigable_layers"), &NavigationAgent3D::set_navigable_layers); - ClassDB::bind_method(D_METHOD("get_navigable_layers"), &NavigationAgent3D::get_navigable_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent3D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent3D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent3D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent3D::get_navigation_map); ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent3D::set_target_location); ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent3D::get_target_location); @@ -92,7 +95,7 @@ void NavigationAgent3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "0.01,100,0.1,suffix:m"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_y"), "set_ignore_y", "get_ignore_y"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigable_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigable_layers", "get_navigable_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); @@ -102,18 +105,32 @@ void NavigationAgent3D::_bind_methods() { void NavigationAgent3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { - agent_parent = Object::cast_to<Node3D>(get_parent()); - if (agent_parent != 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(), agent_parent->get_world_3d()->get_navigation_map()); - set_avoidance_enabled(avoidance_enabled); - } + case NOTIFICATION_POST_ENTER_TREE: { + // need to use POST_ENTER_TREE cause with normal ENTER_TREE not all required Nodes are ready. + // cannot use READY as ready does not get called if Node is readded to SceneTree + set_agent_parent(get_parent()); set_physics_process_internal(true); } break; + case NOTIFICATION_PARENTED: { + if (is_inside_tree() && (get_parent() != agent_parent)) { + // only react to PARENTED notifications when already inside_tree and parent changed, e.g. users switch nodes around + // PARENTED notification fires also when Node is added in scripts to a parent + // this would spam transforms fails and world fails while Node is outside SceneTree + // when node gets reparented when joining the tree POST_ENTER_TREE takes care of this + set_agent_parent(get_parent()); + set_physics_process_internal(true); + } + } break; + + case NOTIFICATION_UNPARENTED: { + // if agent has no parent no point in processing it until reparented + set_agent_parent(nullptr); + set_physics_process_internal(false); + } break; + case NOTIFICATION_EXIT_TREE: { - agent_parent = nullptr; + set_agent_parent(nullptr); set_physics_process_internal(false); } break; @@ -139,7 +156,11 @@ void NavigationAgent3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { - NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); + if (avoidance_enabled) { + // agent_position on NavigationServer is avoidance only and has nothing to do with pathfinding + // no point in flooding NavigationServer queue with agent position updates that get send to the void if avoidance is not used + NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); + } _check_distance_to_target(); } } break; @@ -174,16 +195,50 @@ bool NavigationAgent3D::get_avoidance_enabled() const { return avoidance_enabled; } -void NavigationAgent3D::set_navigable_layers(uint32_t p_layers) { - bool layers_changed = navigable_layers != p_layers; - navigable_layers = p_layers; - if (layers_changed) { +void NavigationAgent3D::set_agent_parent(Node *p_agent_parent) { + // remove agent from any avoidance map before changing parent or there will be leftovers on the RVO map + NavigationServer3D::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + if (Object::cast_to<Node3D>(p_agent_parent) != nullptr) { + // place agent on navigation map first or else the RVO agent callback creation fails silently later + agent_parent = 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(), agent_parent->get_world_3d()->get_navigation_map()); + } + // create new avoidance callback if enabled + set_avoidance_enabled(avoidance_enabled); + } else { + agent_parent = nullptr; + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationAgent3D::set_navigation_layers(uint32_t p_navigation_layers) { + bool navigation_layers_changed = navigation_layers != p_navigation_layers; + navigation_layers = p_navigation_layers; + if (navigation_layers_changed) { _request_repath(); } } -uint32_t NavigationAgent3D::get_navigable_layers() const { - return navigable_layers; +uint32_t NavigationAgent3D::get_navigation_layers() const { + return navigation_layers; +} + +void NavigationAgent3D::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + NavigationServer3D::get_singleton()->agent_set_map(agent, map_override); + _request_repath(); +} + +RID NavigationAgent3D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (agent_parent != nullptr) { + return agent_parent->get_world_3d()->get_navigation_map(); + } + return RID(); } void NavigationAgent3D::set_target_desired_distance(real_t p_dd) { @@ -344,7 +399,11 @@ void NavigationAgent3D::update_navigation() { } if (reload_path) { - navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true, navigable_layers); + if (map_override.is_valid()) { + navigation_path = NavigationServer3D::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); + } else { + navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true, navigation_layers); + } navigation_finished = false; nav_path_index = 0; emit_signal(SNAME("path_changed")); |