Improve NavigationObstacle3D usability

Fix NavigationObstacle3D to be attached to navigation map - without it
the NavigationObstacle3D is not working.

Replace radius approximation algorithm with simple "radius" property.
This commit is contained in:
Pawel Lampe 2021-10-21 22:25:06 +02:00
parent bbcf8ac672
commit fa26fb865f
6 changed files with 156 additions and 24 deletions

View File

@ -8,4 +8,12 @@
</description>
<tutorials>
</tutorials>
<members>
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the agent. Used only if [member estimate_radius] is set to false.
</member>
</members>
</class>

View File

@ -8,4 +8,12 @@
</description>
<tutorials>
</tutorials>
<members>
<member name="estimate_radius" type="bool" setter="set_estimate_radius" getter="is_radius_estimated" default="true">
Enables radius estimation algorithm which uses parent's collision shapes to determine the obstacle radius.
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="1.0">
The radius of the agent. Used only if [member estimate_radius] is set to false.
</member>
</members>
</class>

View File

@ -34,19 +34,41 @@
#include "servers/navigation_server_2d.h"
void NavigationObstacle2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_estimate_radius", "estimate_radius"), &NavigationObstacle2D::set_estimate_radius);
ClassDB::bind_method(D_METHOD("is_radius_estimated"), &NavigationObstacle2D::is_radius_estimated);
ClassDB::bind_method(D_METHOD("set_radius", "radius"), &NavigationObstacle2D::set_radius);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle2D::get_radius);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,500,0.01"), "set_radius", "get_radius");
}
void NavigationObstacle2D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "radius") {
if (estimate_radius) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
}
void NavigationObstacle2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
initialize_agent();
parent_node2d = Object::cast_to<Node2D>(get_parent());
if (parent_node2d != 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(), parent_node2d->get_world_2d()->get_navigation_map());
}
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
parent_node2d = nullptr;
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
parent_node2d = Object::cast_to<Node2D>(get_parent());
update_agent_shape();
reevaluate_agent_radius();
} break;
case NOTIFICATION_UNPARENTED: {
parent_node2d = nullptr;
@ -78,7 +100,22 @@ TypedArray<String> NavigationObstacle2D::get_configuration_warnings() const {
return warnings;
}
void NavigationObstacle2D::update_agent_shape() {
void NavigationObstacle2D::initialize_agent() {
NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0);
NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0);
NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0);
}
void NavigationObstacle2D::reevaluate_agent_radius() {
if (!estimate_radius) {
NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
} else if (parent_node2d) {
NavigationServer2D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
}
}
real_t NavigationObstacle2D::estimate_agent_radius() const {
if (parent_node2d) {
// Estimate the radius of this physics body
real_t radius = 0.0;
@ -101,15 +138,21 @@ void NavigationObstacle2D::update_agent_shape() {
Vector2 s = parent_node2d->get_global_scale();
radius *= MAX(s.x, s.y);
if (radius == 0.0) {
radius = 1.0; // Never a 0 radius
if (radius > 0.0) {
return radius;
}
// Initialize the Agent as an object
NavigationServer2D::get_singleton()->agent_set_neighbor_dist(agent, 0.0);
NavigationServer2D::get_singleton()->agent_set_max_neighbors(agent, 0);
NavigationServer2D::get_singleton()->agent_set_time_horizon(agent, 0.0);
NavigationServer2D::get_singleton()->agent_set_radius(agent, radius);
NavigationServer2D::get_singleton()->agent_set_max_speed(agent, 0.0);
}
return 1.0; // Never a 0 radius
}
void NavigationObstacle2D::set_estimate_radius(bool p_estimate_radius) {
estimate_radius = p_estimate_radius;
notify_property_list_changed();
reevaluate_agent_radius();
}
void NavigationObstacle2D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
radius = p_radius;
reevaluate_agent_radius();
}

View File

@ -40,8 +40,12 @@ class NavigationObstacle2D : public Node {
Node2D *parent_node2d = nullptr;
RID agent;
bool estimate_radius = true;
real_t radius = 1.0;
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const override;
void _notification(int p_what);
public:
@ -52,10 +56,21 @@ public:
return agent;
}
void set_estimate_radius(bool p_estimate_radius);
bool is_radius_estimated() const {
return estimate_radius;
}
void set_radius(real_t p_radius);
real_t get_radius() const {
return radius;
}
TypedArray<String> get_configuration_warnings() const override;
private:
void update_agent_shape();
void initialize_agent();
void reevaluate_agent_radius();
real_t estimate_agent_radius() const;
};
#endif

View File

@ -35,19 +35,41 @@
#include "servers/navigation_server_3d.h"
void NavigationObstacle3D::_bind_methods() {
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);
ClassDB::bind_method(D_METHOD("get_radius"), &NavigationObstacle3D::get_radius);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "estimate_radius"), "set_estimate_radius", "is_radius_estimated");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_radius", "get_radius");
}
void NavigationObstacle3D::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "radius") {
if (estimate_radius) {
p_property.usage = PROPERTY_USAGE_NOEDITOR;
}
}
}
void NavigationObstacle3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY: {
initialize_agent();
parent_node3d = Object::cast_to<Node3D>(get_parent());
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());
}
set_physics_process_internal(true);
} break;
case NOTIFICATION_EXIT_TREE: {
parent_node3d = nullptr;
set_physics_process_internal(false);
} break;
case NOTIFICATION_PARENTED: {
parent_node3d = Object::cast_to<Node3D>(get_parent());
update_agent_shape();
reevaluate_agent_radius();
} break;
case NOTIFICATION_UNPARENTED: {
parent_node3d = nullptr;
@ -86,7 +108,22 @@ TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const {
return warnings;
}
void NavigationObstacle3D::update_agent_shape() {
void NavigationObstacle3D::initialize_agent() {
NavigationServer3D::get_singleton()->agent_set_neighbor_dist(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);
}
void NavigationObstacle3D::reevaluate_agent_radius() {
if (!estimate_radius) {
NavigationServer3D::get_singleton()->agent_set_radius(agent, radius);
} else if (parent_node3d) {
NavigationServer3D::get_singleton()->agent_set_radius(agent, estimate_agent_radius());
}
}
real_t NavigationObstacle3D::estimate_agent_radius() const {
if (parent_node3d) {
// Estimate the radius of this physics body
real_t radius = 0.0;
@ -110,15 +147,21 @@ void NavigationObstacle3D::update_agent_shape() {
Vector3 s = parent_node3d->get_global_transform().basis.get_scale();
radius *= MAX(s.x, MAX(s.y, s.z));
if (radius == 0.0) {
radius = 1.0; // Never a 0 radius
if (radius > 0.0) {
return radius;
}
// Initialize the Agent as an object
NavigationServer3D::get_singleton()->agent_set_neighbor_dist(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_radius(agent, radius);
NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0);
}
return 1.0; // Never a 0 radius
}
void NavigationObstacle3D::set_estimate_radius(bool p_estimate_radius) {
estimate_radius = p_estimate_radius;
notify_property_list_changed();
reevaluate_agent_radius();
}
void NavigationObstacle3D::set_radius(real_t p_radius) {
ERR_FAIL_COND_MSG(p_radius <= 0.0, "Radius must be greater than 0.");
radius = p_radius;
reevaluate_agent_radius();
}

View File

@ -39,8 +39,12 @@ class NavigationObstacle3D : public Node {
Node3D *parent_node3d = nullptr;
RID agent;
bool estimate_radius = true;
real_t radius = 1.0;
protected:
static void _bind_methods();
void _validate_property(PropertyInfo &p_property) const override;
void _notification(int p_what);
public:
@ -51,10 +55,21 @@ public:
return agent;
}
void set_estimate_radius(bool p_estimate_radius);
bool is_radius_estimated() const {
return estimate_radius;
}
void set_radius(real_t p_radius);
real_t get_radius() const {
return radius;
}
TypedArray<String> get_configuration_warnings() const override;
private:
void update_agent_shape();
void initialize_agent();
void reevaluate_agent_radius();
real_t estimate_agent_radius() const;
};
#endif