diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml index daeb7b0ff66..24325b4a014 100644 --- a/doc/classes/NavigationAgent3D.xml +++ b/doc/classes/NavigationAgent3D.xml @@ -167,6 +167,9 @@ The height of the avoidance agent. Agents will ignore other agents or obstacles that are above or below their current position + height in 2D avoidance. Does nothing in 3D avoidance which uses radius spheres alone. + + If [code]true[/code] and the agent uses 2D avoidance it will remember the set y-axis velocity and reapply it after the avoidance step. While 2D avoidance has no y-axis and simulates on a flat plane this setting can help mitigate the most obvious clipping on uneven 3D geometry. + The maximum number of neighbors for the agent to consider. diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 3c119671ec1..87b8087e44b 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -57,6 +57,9 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_3d_avoidance", "enabled"), &NavigationAgent3D::set_use_3d_avoidance); ClassDB::bind_method(D_METHOD("get_use_3d_avoidance"), &NavigationAgent3D::get_use_3d_avoidance); + ClassDB::bind_method(D_METHOD("set_keep_y_velocity", "enabled"), &NavigationAgent3D::set_keep_y_velocity); + ClassDB::bind_method(D_METHOD("get_keep_y_velocity"), &NavigationAgent3D::get_keep_y_velocity); + ClassDB::bind_method(D_METHOD("set_neighbor_distance", "neighbor_distance"), &NavigationAgent3D::set_neighbor_distance); ClassDB::bind_method(D_METHOD("get_neighbor_distance"), &NavigationAgent3D::get_neighbor_distance); @@ -149,6 +152,7 @@ void NavigationAgent3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon_obstacles", PROPERTY_HINT_RANGE, "0.0,10,0.01,or_greater,suffix:s"), "set_time_horizon_obstacles", "get_time_horizon_obstacles"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.01,10000,0.01,or_greater,suffix:m/s"), "set_max_speed", "get_max_speed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_3d_avoidance"), "set_use_3d_avoidance", "get_use_3d_avoidance"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_y_velocity"), "set_keep_y_velocity", "get_keep_y_velocity"); ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_layers", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_layers", "get_avoidance_layers"); ADD_PROPERTY(PropertyInfo(Variant::INT, "avoidance_mask", PROPERTY_HINT_LAYERS_AVOIDANCE), "set_avoidance_mask", "get_avoidance_mask"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "avoidance_priority", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_avoidance_priority", "get_avoidance_priority"); @@ -281,7 +285,9 @@ void NavigationAgent3D::_notification(int p_what) { velocity_submitted = false; if (avoidance_enabled) { if (!use_3d_avoidance) { - stored_y_velocity = velocity.y; + if (keep_y_velocity) { + stored_y_velocity = velocity.y; + } velocity.y = 0.0; } NavigationServer3D::get_singleton()->agent_set_velocity(agent, velocity); @@ -304,6 +310,15 @@ void NavigationAgent3D::_notification(int p_what) { } } +void NavigationAgent3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "keep_y_velocity") { + if (use_3d_avoidance) { + p_property.usage = PROPERTY_USAGE_NONE; + return; + } + } +} + NavigationAgent3D::NavigationAgent3D() { agent = NavigationServer3D::get_singleton()->agent_create(); @@ -523,6 +538,15 @@ void NavigationAgent3D::set_use_3d_avoidance(bool p_use_3d_avoidance) { notify_property_list_changed(); } +void NavigationAgent3D::set_keep_y_velocity(bool p_enabled) { + keep_y_velocity = p_enabled; + stored_y_velocity = 0.0; +} + +bool NavigationAgent3D::get_keep_y_velocity() const { + return keep_y_velocity; +} + void NavigationAgent3D::set_neighbor_distance(real_t p_distance) { if (Math::is_equal_approx(neighbor_distance, p_distance)) { return; @@ -807,6 +831,7 @@ void NavigationAgent3D::update_navigation() { NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); NavigationServer3D::get_singleton()->agent_set_velocity(agent, Vector3(0.0, 0.0, 0.0)); NavigationServer3D::get_singleton()->agent_set_velocity_forced(agent, Vector3(0.0, 0.0, 0.0)); + stored_y_velocity = 0.0; } emit_signal(SNAME("navigation_finished")); break; diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index 3032942cddc..ff0498a2a8f 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -89,6 +89,7 @@ class NavigationAgent3D : public Node { // 2D avoidance has no y-axis. This stores and reapplies the y-axis velocity to the agent before and after the avoidance step. // While not perfect it at least looks way better than agent's that clip through everything that is not a flat surface + bool keep_y_velocity = true; float stored_y_velocity = 0.0; bool target_position_submitted = false; @@ -114,6 +115,7 @@ class NavigationAgent3D : public Node { protected: static void _bind_methods(); void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; #ifndef DISABLE_DEPRECATED bool _set(const StringName &p_name, const Variant &p_value); @@ -173,6 +175,9 @@ public: void set_use_3d_avoidance(bool p_use_3d_avoidance); bool get_use_3d_avoidance() const { return use_3d_avoidance; } + void set_keep_y_velocity(bool p_enabled); + bool get_keep_y_velocity() const; + void set_neighbor_distance(real_t p_distance); real_t get_neighbor_distance() const { return neighbor_distance; }