diff --git a/doc/classes/NavigationAgent3D.xml b/doc/classes/NavigationAgent3D.xml
index daeb7b0ff663..24325b4a0147 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 3c119671ec13..87b8087e44ba 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 3032942cddc8..ff0498a2a8f8 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 @@ class NavigationAgent3D : public Node {
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; }