Skip to content

Commit

Permalink
Add NavigationAgent Path Debug Visualization
Browse files Browse the repository at this point in the history
Adds path debug visuals for NavigationAgent2D, NavigationAgent3D and NavigationServer.
  • Loading branch information
smix8 committed Jan 31, 2023
1 parent 8612c12 commit 3b28fee
Show file tree
Hide file tree
Showing 12 changed files with 618 additions and 0 deletions.
15 changes: 15 additions & 0 deletions doc/classes/NavigationAgent2D.xml
Expand Up @@ -111,6 +111,21 @@
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer2D]. When [method NavigationAgent2D.set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector2 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
</member>
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
If [code]true[/code] shows debug visuals for this agent.
</member>
<member name="debug_path_custom_color" type="Color" setter="set_debug_path_custom_color" getter="get_debug_path_custom_color" default="Color(1, 1, 1, 1)">
If [member debug_use_custom] is [code]true[/code] uses this color for this agent instead of global color.
</member>
<member name="debug_path_custom_line_width" type="float" setter="set_debug_path_custom_line_width" getter="get_debug_path_custom_line_width" default="1.0">
If [member debug_use_custom] is [code]true[/code] uses this line width for rendering paths for this agent instead of global line width.
</member>
<member name="debug_path_custom_point_size" type="float" setter="set_debug_path_custom_point_size" getter="get_debug_path_custom_point_size" default="4.0">
If [member debug_use_custom] is [code]true[/code] uses this rasterized point size for rendering path points for this agent instead of global point size.
</member>
<member name="debug_use_custom" type="bool" setter="set_debug_use_custom" getter="get_debug_use_custom" default="false">
If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color.
</member>
<member name="max_neighbors" type="int" setter="set_max_neighbors" getter="get_max_neighbors" default="10">
The maximum number of neighbors for the agent to consider.
</member>
Expand Down
12 changes: 12 additions & 0 deletions doc/classes/NavigationAgent3D.xml
Expand Up @@ -114,6 +114,18 @@
<member name="avoidance_enabled" type="bool" setter="set_avoidance_enabled" getter="get_avoidance_enabled" default="false">
If [code]true[/code] the agent is registered for an RVO avoidance callback on the [NavigationServer3D]. When [method NavigationAgent3D.set_velocity] is used and the processing is completed a [code]safe_velocity[/code] Vector3 is received with a signal connection to [signal velocity_computed]. Avoidance processing with many registered agents has a significant performance cost and should only be enabled on agents that currently require it.
</member>
<member name="debug_enabled" type="bool" setter="set_debug_enabled" getter="get_debug_enabled" default="false">
If [code]true[/code] shows debug visuals for this agent.
</member>
<member name="debug_path_custom_color" type="Color" setter="set_debug_path_custom_color" getter="get_debug_path_custom_color" default="Color(1, 1, 1, 1)">
If [member debug_use_custom] is [code]true[/code] uses this color for this agent instead of global color.
</member>
<member name="debug_path_custom_point_size" type="float" setter="set_debug_path_custom_point_size" getter="get_debug_path_custom_point_size" default="4.0">
If [member debug_use_custom] is [code]true[/code] uses this rasterized point size for rendering path points for this agent instead of global point size.
</member>
<member name="debug_use_custom" type="bool" setter="set_debug_use_custom" getter="get_debug_use_custom" default="false">
If [code]true[/code] uses the defined [member debug_path_custom_color] for this agent instead of global color.
</member>
<member name="ignore_y" type="bool" setter="set_ignore_y" getter="get_ignore_y" default="true">
Ignores collisions on the Y axis. Must be true to move on a horizontal plane.
</member>
Expand Down
5 changes: 5 additions & 0 deletions doc/classes/NavigationServer2D.xml
Expand Up @@ -528,5 +528,10 @@
Emitted when a navigation map is updated, when a region moves or is modified.
</description>
</signal>
<signal name="navigation_debug_changed">
<description>
Emitted when navigation debug settings are changed. Only available in debug builds.
</description>
</signal>
</signals>
</class>
12 changes: 12 additions & 0 deletions doc/classes/ProjectSettings.xml
Expand Up @@ -528,9 +528,21 @@
<member name="debug/shapes/collision/shape_color" type="Color" setter="" getter="" default="Color(0, 0.6, 0.7, 0.42)">
Color of the collision shapes, visible when "Visible Collision Shapes" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/agent_path_color" type="Color" setter="" getter="" default="Color(1, 0, 0, 1)">
Color to display enabled navigation agent paths when an agent has debug enabled.
</member>
<member name="debug/shapes/navigation/agent_path_point_size" type="float" setter="" getter="" default="4.0">
Rasterized size (pixel) used to render navigation agent path points when an agent has debug enabled.
</member>
<member name="debug/shapes/navigation/edge_connection_color" type="Color" setter="" getter="" default="Color(1, 0, 1, 1)">
Color to display edge connections between navigation regions, visible when "Visible Navigation" is enabled in the Debug menu.
</member>
<member name="debug/shapes/navigation/enable_agent_paths" type="bool" setter="" getter="" default="true">
If enabled, displays navigation agent paths when an agent has debug enabled.
</member>
<member name="debug/shapes/navigation/enable_agent_paths_xray" type="bool" setter="" getter="" default="true">
If enabled, displays navigation agent paths through geometry when an agent has debug enabled.
</member>
<member name="debug/shapes/navigation/enable_edge_connections" type="bool" setter="" getter="" default="true">
If enabled, displays edge connections between navigation regions when "Visible Navigation" is enabled in the Debug menu.
</member>
Expand Down
162 changes: 162 additions & 0 deletions scene/2d/navigation_agent_2d.cpp
Expand Up @@ -108,6 +108,26 @@ void NavigationAgent2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_horizon", PROPERTY_HINT_RANGE, "0.1,10,0.01,suffix:s"), "set_time_horizon", "get_time_horizon");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_speed", PROPERTY_HINT_RANGE, "0.1,10000,0.01,suffix:px/s"), "set_max_speed", "get_max_speed");

#ifdef DEBUG_ENABLED
ClassDB::bind_method(D_METHOD("set_debug_enabled", "enabled"), &NavigationAgent2D::set_debug_enabled);
ClassDB::bind_method(D_METHOD("get_debug_enabled"), &NavigationAgent2D::get_debug_enabled);
ClassDB::bind_method(D_METHOD("set_debug_use_custom", "enabled"), &NavigationAgent2D::set_debug_use_custom);
ClassDB::bind_method(D_METHOD("get_debug_use_custom"), &NavigationAgent2D::get_debug_use_custom);
ClassDB::bind_method(D_METHOD("set_debug_path_custom_color", "color"), &NavigationAgent2D::set_debug_path_custom_color);
ClassDB::bind_method(D_METHOD("get_debug_path_custom_color"), &NavigationAgent2D::get_debug_path_custom_color);
ClassDB::bind_method(D_METHOD("set_debug_path_custom_point_size", "point_size"), &NavigationAgent2D::set_debug_path_custom_point_size);
ClassDB::bind_method(D_METHOD("get_debug_path_custom_point_size"), &NavigationAgent2D::get_debug_path_custom_point_size);
ClassDB::bind_method(D_METHOD("set_debug_path_custom_line_width", "line_width"), &NavigationAgent2D::set_debug_path_custom_line_width);
ClassDB::bind_method(D_METHOD("get_debug_path_custom_line_width"), &NavigationAgent2D::get_debug_path_custom_line_width);

ADD_GROUP("Debug", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_enabled"), "set_debug_enabled", "get_debug_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_use_custom"), "set_debug_use_custom", "get_debug_use_custom");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_path_custom_color"), "set_debug_path_custom_color", "get_debug_path_custom_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_point_size", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_point_size", "get_debug_path_custom_point_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "debug_path_custom_line_width", PROPERTY_HINT_RANGE, "1,50,1,suffix:px"), "set_debug_path_custom_line_width", "get_debug_path_custom_line_width");
#endif // DEBUG_ENABLED

ADD_SIGNAL(MethodInfo("path_changed"));
ADD_SIGNAL(MethodInfo("target_reached"));
ADD_SIGNAL(MethodInfo("waypoint_reached", PropertyInfo(Variant::DICTIONARY, "details")));
Expand All @@ -123,6 +143,12 @@ void NavigationAgent2D::_notification(int p_what) {
// 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);

#ifdef DEBUG_ENABLED
if (NavigationServer2D::get_singleton()->get_debug_enabled()) {
debug_path_dirty = true;
}
#endif // DEBUG_ENABLED
} break;

case NOTIFICATION_PARENTED: {
Expand Down Expand Up @@ -165,6 +191,12 @@ void NavigationAgent2D::_notification(int p_what) {
case NOTIFICATION_EXIT_TREE: {
agent_parent = nullptr;
set_physics_process_internal(false);

#ifdef DEBUG_ENABLED
if (debug_path_instance.is_valid()) {
RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, false);
}
#endif // DEBUG_ENABLED
} break;

case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
Expand All @@ -176,6 +208,12 @@ void NavigationAgent2D::_notification(int p_what) {
}
_check_distance_to_target();
}

#ifdef DEBUG_ENABLED
if (debug_path_dirty) {
_update_debug_path();
}
#endif // DEBUG_ENABLED
} break;
}
}
Expand All @@ -194,12 +232,25 @@ NavigationAgent2D::NavigationAgent2D() {

navigation_result = Ref<NavigationPathQueryResult2D>();
navigation_result.instantiate();

#ifdef DEBUG_ENABLED
NavigationServer2D::get_singleton()->connect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent2D::_navigation_debug_changed));
#endif // DEBUG_ENABLED
}

NavigationAgent2D::~NavigationAgent2D() {
ERR_FAIL_NULL(NavigationServer2D::get_singleton());
NavigationServer2D::get_singleton()->free(agent);
agent = RID(); // Pointless

#ifdef DEBUG_ENABLED
NavigationServer2D::get_singleton()->disconnect(SNAME("navigation_debug_changed"), callable_mp(this, &NavigationAgent2D::_navigation_debug_changed));

ERR_FAIL_NULL(RenderingServer::get_singleton());
if (debug_path_instance.is_valid()) {
RenderingServer::get_singleton()->free(debug_path_instance);
}
#endif // DEBUG_ENABLED
}

void NavigationAgent2D::set_avoidance_enabled(bool p_enabled) {
Expand Down Expand Up @@ -463,6 +514,9 @@ void NavigationAgent2D::update_navigation() {
}

NavigationServer2D::get_singleton()->query_path(navigation_query, navigation_result);
#ifdef DEBUG_ENABLED
debug_path_dirty = true;
#endif // DEBUG_ENABLED
navigation_finished = false;
navigation_path_index = 0;
emit_signal(SNAME("path_changed"));
Expand Down Expand Up @@ -549,3 +603,111 @@ void NavigationAgent2D::_check_distance_to_target() {
}
}
}

////////DEBUG////////////////////////////////////////////////////////////

#ifdef DEBUG_ENABLED
void NavigationAgent2D::set_debug_enabled(bool p_enabled) {
debug_enabled = p_enabled;
debug_path_dirty = true;
}

bool NavigationAgent2D::get_debug_enabled() const {
return debug_enabled;
}

void NavigationAgent2D::set_debug_use_custom(bool p_enabled) {
debug_use_custom = p_enabled;
debug_path_dirty = true;
}

bool NavigationAgent2D::get_debug_use_custom() const {
return debug_use_custom;
}

void NavigationAgent2D::set_debug_path_custom_color(Color p_color) {
debug_path_custom_color = p_color;
debug_path_dirty = true;
}

Color NavigationAgent2D::get_debug_path_custom_color() const {
return debug_path_custom_color;
}

void NavigationAgent2D::set_debug_path_custom_point_size(float p_point_size) {
debug_path_custom_point_size = MAX(0.1, p_point_size);
debug_path_dirty = true;
}

float NavigationAgent2D::get_debug_path_custom_point_size() const {
return debug_path_custom_point_size;
}

void NavigationAgent2D::set_debug_path_custom_line_width(float p_line_width) {
debug_path_custom_line_width = p_line_width;
debug_path_dirty = true;
}

float NavigationAgent2D::get_debug_path_custom_line_width() const {
return debug_path_custom_line_width;
}

void NavigationAgent2D::_navigation_debug_changed() {
debug_path_dirty = true;
}

void NavigationAgent2D::_update_debug_path() {
if (!debug_path_dirty) {
return;
}
debug_path_dirty = false;

if (!debug_path_instance.is_valid()) {
debug_path_instance = RenderingServer::get_singleton()->canvas_item_create();
}

RenderingServer::get_singleton()->canvas_item_clear(debug_path_instance);

if (!(debug_enabled && NavigationServer2D::get_singleton()->get_debug_navigation_enable_agent_paths())) {
return;
}

if (!(agent_parent && agent_parent->is_inside_tree())) {
return;
}

RenderingServer::get_singleton()->canvas_item_set_parent(debug_path_instance, agent_parent->get_canvas());
RenderingServer::get_singleton()->canvas_item_set_visible(debug_path_instance, agent_parent->is_visible_in_tree());

const Vector<Vector2> &navigation_path = navigation_result->get_path();

if (navigation_path.size() <= 1) {
return;
}

Color debug_path_color = NavigationServer2D::get_singleton()->get_debug_navigation_agent_path_color();
if (debug_use_custom) {
debug_path_color = debug_path_custom_color;
}

Vector<Color> debug_path_colors;
debug_path_colors.resize(navigation_path.size());
debug_path_colors.fill(debug_path_color);

RenderingServer::get_singleton()->canvas_item_add_polyline(debug_path_instance, navigation_path, debug_path_colors, debug_path_custom_line_width, false);

float point_size = NavigationServer2D::get_singleton()->get_debug_navigation_agent_path_point_size();
float half_point_size = point_size * 0.5;

if (debug_use_custom) {
point_size = debug_path_custom_point_size;
half_point_size = debug_path_custom_point_size * 0.5;
}

for (int i = 0; i < navigation_path.size(); i++) {
const Vector2 &vert = navigation_path[i];
Rect2 path_point_rect = Rect2(vert.x - half_point_size, vert.y - half_point_size, point_size, point_size);
RenderingServer::get_singleton()->canvas_item_add_rect(debug_path_instance, path_point_rect, debug_path_color);
}
}
#endif // DEBUG_ENABLED
31 changes: 31 additions & 0 deletions scene/2d/navigation_agent_2d.h
Expand Up @@ -74,6 +74,20 @@ class NavigationAgent2D : public Node {
// No initialized on purpose
uint32_t update_frame_id = 0;

#ifdef DEBUG_ENABLED
bool debug_enabled = false;
bool debug_path_dirty = true;
RID debug_path_instance;
float debug_path_custom_point_size = 4.0;
float debug_path_custom_line_width = 1.0;
bool debug_use_custom = false;
Color debug_path_custom_color = Color(1.0, 1.0, 1.0, 1.0);

private:
void _navigation_debug_changed();
void _update_debug_path();
#endif // DEBUG_ENABLED

protected:
static void _bind_methods();
void _notification(int p_what);
Expand Down Expand Up @@ -169,6 +183,23 @@ class NavigationAgent2D : public Node {

PackedStringArray get_configuration_warnings() const override;

#ifdef DEBUG_ENABLED
void set_debug_enabled(bool p_enabled);
bool get_debug_enabled() const;

void set_debug_use_custom(bool p_enabled);
bool get_debug_use_custom() const;

void set_debug_path_custom_color(Color p_color);
Color get_debug_path_custom_color() const;

void set_debug_path_custom_point_size(float p_point_size);
float get_debug_path_custom_point_size() const;

void set_debug_path_custom_line_width(float p_line_width);
float get_debug_path_custom_line_width() const;
#endif // DEBUG_ENABLED

private:
void update_navigation();
void _request_repath();
Expand Down

0 comments on commit 3b28fee

Please sign in to comment.