diff options
Diffstat (limited to 'scene/2d/navigation_agent_2d.cpp')
-rw-r--r-- | scene/2d/navigation_agent_2d.cpp | 95 |
1 files changed, 77 insertions, 18 deletions
diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index 00082b321e..e2ab4f7538 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -61,8 +61,11 @@ void NavigationAgent2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_path_max_distance", "max_speed"), &NavigationAgent2D::set_path_max_distance); ClassDB::bind_method(D_METHOD("get_path_max_distance"), &NavigationAgent2D::get_path_max_distance); - ClassDB::bind_method(D_METHOD("set_navigable_layers", "navigable_layers"), &NavigationAgent2D::set_navigable_layers); - ClassDB::bind_method(D_METHOD("get_navigable_layers"), &NavigationAgent2D::get_navigable_layers); + ClassDB::bind_method(D_METHOD("set_navigation_layers", "navigation_layers"), &NavigationAgent2D::set_navigation_layers); + ClassDB::bind_method(D_METHOD("get_navigation_layers"), &NavigationAgent2D::get_navigation_layers); + + ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationAgent2D::set_navigation_map); + ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationAgent2D::get_navigation_map); ClassDB::bind_method(D_METHOD("set_target_location", "location"), &NavigationAgent2D::set_target_location); ClassDB::bind_method(D_METHOD("get_target_location"), &NavigationAgent2D::get_target_location); @@ -86,7 +89,7 @@ void NavigationAgent2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,100000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "path_max_distance", PROPERTY_HINT_RANGE, "10,100,1,suffix:px"), "set_path_max_distance", "get_path_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "avoidance_enabled"), "set_avoidance_enabled", "get_avoidance_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "navigable_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigable_layers", "get_navigable_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "navigation_layers", PROPERTY_HINT_LAYERS_2D_NAVIGATION), "set_navigation_layers", "get_navigation_layers"); ADD_SIGNAL(MethodInfo("path_changed")); ADD_SIGNAL(MethodInfo("target_reached")); @@ -96,16 +99,30 @@ void NavigationAgent2D::_bind_methods() { void NavigationAgent2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: { - agent_parent = Object::cast_to<Node2D>(get_parent()); - if (agent_parent != nullptr) { - // place agent on navigation map first or else the RVO agent callback creation fails silently later - NavigationServer2D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->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_PAUSED: { if (agent_parent && !agent_parent->can_process()) { map_before_pause = NavigationServer2D::get_singleton()->agent_get_map(get_rid()); @@ -133,7 +150,11 @@ void NavigationAgent2D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { - NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position()); + 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 + NavigationServer2D::get_singleton()->agent_set_position(agent, agent_parent->get_global_position()); + } _check_distance_to_target(); } } break; @@ -167,16 +188,50 @@ bool NavigationAgent2D::get_avoidance_enabled() const { return avoidance_enabled; } -void NavigationAgent2D::set_navigable_layers(uint32_t p_layers) { - bool layers_changed = navigable_layers != p_layers; - navigable_layers = p_layers; - if (layers_changed) { +void NavigationAgent2D::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 + NavigationServer2D::get_singleton()->agent_set_callback(agent, nullptr, "_avoidance_done"); + if (Object::cast_to<Node2D>(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<Node2D>(p_agent_parent); + if (map_override.is_valid()) { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), map_override); + } else { + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_2d()->get_navigation_map()); + } + // create new avoidance callback if enabled + set_avoidance_enabled(avoidance_enabled); + } else { + agent_parent = nullptr; + NavigationServer2D::get_singleton()->agent_set_map(get_rid(), RID()); + } +} + +void NavigationAgent2D::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 NavigationAgent2D::get_navigable_layers() const { - return navigable_layers; +uint32_t NavigationAgent2D::get_navigation_layers() const { + return navigation_layers; +} + +void NavigationAgent2D::set_navigation_map(RID p_navigation_map) { + map_override = p_navigation_map; + NavigationServer2D::get_singleton()->agent_set_map(agent, map_override); + _request_repath(); +} + +RID NavigationAgent2D::get_navigation_map() const { + if (map_override.is_valid()) { + return map_override; + } else if (agent_parent != nullptr) { + return agent_parent->get_world_2d()->get_navigation_map(); + } + return RID(); } void NavigationAgent2D::set_target_desired_distance(real_t p_dd) { @@ -327,7 +382,11 @@ void NavigationAgent2D::update_navigation() { } if (reload_path) { - navigation_path = NavigationServer2D::get_singleton()->map_get_path(agent_parent->get_world_2d()->get_navigation_map(), o, target_location, true, navigable_layers); + if (map_override.is_valid()) { + navigation_path = NavigationServer2D::get_singleton()->map_get_path(map_override, o, target_location, true, navigation_layers); + } else { + navigation_path = NavigationServer2D::get_singleton()->map_get_path(agent_parent->get_world_2d()->get_navigation_map(), o, target_location, true, navigation_layers); + } navigation_finished = false; nav_path_index = 0; emit_signal(SNAME("path_changed")); |