1
0
mirror of https://github.com/godotengine/godot.git synced 2026-01-05 19:31:35 +00:00

Fix AStars to return empty path for disabled from point

This commit is contained in:
kleonc
2025-12-13 15:28:29 +01:00
parent 08e6cd181f
commit 4c3cf9c1c4
7 changed files with 77 additions and 80 deletions

View File

@@ -306,11 +306,17 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const {
return closest_point;
}
bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_path) {
bool AStar3D::_solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path) {
last_closest_point = nullptr;
pass++;
if (!end_point->enabled && !p_allow_partial_path) {
if (!p_begin_point->enabled) {
return false;
}
if (p_begin_point == p_end_point) {
return true;
}
if (!p_end_point->enabled && !p_allow_partial_path) {
return false;
}
@@ -319,11 +325,11 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_
LocalVector<Point *> open_list;
SortArray<Point *, SortPoints> sorter;
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
begin_point->abs_g_score = 0;
begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
p_begin_point->g_score = 0;
p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
p_begin_point->abs_g_score = 0;
p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
open_list.push_back(p_begin_point);
while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point.
@@ -333,7 +339,7 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_
last_closest_point = p;
}
if (p == end_point) {
if (p == p_end_point) {
found_route = true;
break;
}
@@ -370,7 +376,7 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_
e->prev_point = p;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id);
e->abs_g_score = tentative_g_score;
e->abs_f_score = e->f_score - e->g_score;
@@ -428,12 +434,6 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool
ERR_FAIL_COND_V_MSG(!b_entry, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));
Point *b = *b_entry;
if (a == b) {
Vector<Vector3> ret;
ret.push_back(a->pos);
return ret;
}
Point *begin_point = a;
Point *end_point = b;
@@ -482,16 +482,6 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
ERR_FAIL_COND_V_MSG(!b_entry, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));
Point *b = *b_entry;
if (a == b) {
Vector<int64_t> ret;
ret.push_back(a->id);
return ret;
}
if (!a->enabled) {
return Vector<int64_t>();
}
Point *begin_point = a;
Point *end_point = b;
@@ -738,11 +728,6 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool
ERR_FAIL_COND_V_MSG(!b_entry, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));
AStar3D::Point *b = *b_entry;
if (a == b) {
Vector<Vector2> ret = { Vector2(a->pos.x, a->pos.y) };
return ret;
}
AStar3D::Point *begin_point = a;
AStar3D::Point *end_point = b;
@@ -791,16 +776,6 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
ERR_FAIL_COND_V_MSG(!to_entry, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));
AStar3D::Point *b = *to_entry;
if (a == b) {
Vector<int64_t> ret;
ret.push_back(a->id);
return ret;
}
if (!a->enabled) {
return Vector<int64_t>();
}
AStar3D::Point *begin_point = a;
AStar3D::Point *end_point = b;
@@ -840,11 +815,17 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_
return path;
}
bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path) {
bool AStar2D::_solve(AStar3D::Point *p_begin_point, AStar3D::Point *p_end_point, bool p_allow_partial_path) {
astar.last_closest_point = nullptr;
astar.pass++;
if (!end_point->enabled && !p_allow_partial_path) {
if (!p_begin_point->enabled) {
return false;
}
if (p_begin_point == p_end_point) {
return true;
}
if (!p_end_point->enabled && !p_allow_partial_path) {
return false;
}
@@ -853,11 +834,11 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo
LocalVector<AStar3D::Point *> open_list;
SortArray<AStar3D::Point *, AStar3D::SortPoints> sorter;
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
begin_point->abs_g_score = 0;
begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
p_begin_point->g_score = 0;
p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
p_begin_point->abs_g_score = 0;
p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
open_list.push_back(p_begin_point);
while (!open_list.is_empty()) {
AStar3D::Point *p = open_list[0]; // The currently processed point.
@@ -867,7 +848,7 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo
astar.last_closest_point = p;
}
if (p == end_point) {
if (p == p_end_point) {
found_route = true;
break;
}
@@ -904,7 +885,7 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo
e->prev_point = p;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id);
e->abs_g_score = tentative_g_score;
e->abs_f_score = e->f_score - e->g_score;

View File

@@ -113,7 +113,7 @@ class AStar3D : public RefCounted {
Point *last_closest_point = nullptr;
bool neighbor_filter_enabled = false;
bool _solve(Point *begin_point, Point *end_point, bool p_allow_partial_path);
bool _solve(Point *p_begin_point, Point *p_end_point, bool p_allow_partial_path);
protected:
static void _bind_methods();
@@ -172,7 +172,7 @@ class AStar2D : public RefCounted {
GDCLASS(AStar2D, RefCounted);
AStar3D astar;
bool _solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, bool p_allow_partial_path);
bool _solve(AStar3D::Point *p_begin_point, AStar3D::Point *p_end_point, bool p_allow_partial_path);
protected:
static void _bind_methods();

View File

@@ -495,6 +495,12 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point, bool p_allow_
last_closest_point = nullptr;
pass++;
if (_get_solid_unchecked(p_begin_point->id)) {
return false;
}
if (p_begin_point == p_end_point) {
return true;
}
if (_get_solid_unchecked(p_end_point->id) && !p_allow_partial_path) {
return false;
}
@@ -636,17 +642,8 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
Point *a = _get_point(p_from_id.x, p_from_id.y);
Point *b = _get_point(p_to_id.x, p_to_id.y);
if (a == b) {
Vector<Vector2> ret;
ret.push_back(a->pos);
return ret;
}
Point *begin_point = a;
Point *end_point = b;
Point *begin_point = _get_point(p_from_id.x, p_from_id.y);
Point *end_point = _get_point(p_to_id.x, p_to_id.y);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {
@@ -689,17 +686,8 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
Point *a = _get_point(p_from_id.x, p_from_id.y);
Point *b = _get_point(p_to_id.x, p_to_id.y);
if (a == b) {
TypedArray<Vector2i> ret;
ret.push_back(a->id);
return ret;
}
Point *begin_point = a;
Point *end_point = b;
Point *begin_point = _get_point(p_from_id.x, p_from_id.y);
Point *end_point = _get_point(p_to_id.x, p_to_id.y);
bool found_route = _solve(begin_point, end_point, p_allow_partial_path);
if (!found_route) {

View File

@@ -152,7 +152,8 @@
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]).
If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] When [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish.
[codeblocks]
[gdscript]
@@ -244,7 +245,8 @@
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the points that are in the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]).
If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] This method is not thread-safe; it can only be used from a single [Thread] at a given time. Consider using [Mutex] to ensure exclusive access to one thread to avoid race conditions.
Additionally, when [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish.
</description>

View File

@@ -193,7 +193,8 @@
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the IDs of the points that form the path found by AStar3D between the given points. The array is ordered from the starting point to the ending point of the path.
If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]).
If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] When [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish.
[codeblocks]
[gdscript]
@@ -283,7 +284,8 @@
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the points that are in the path found by AStar3D between the given points. The array is ordered from the starting point to the ending point of the path.
If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]).
If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] This method is not thread-safe; it can only be used from a single [Thread] at a given time. Consider using [Mutex] to ensure exclusive access to one thread to avoid race conditions.
Additionally, when [param allow_partial_path] is [code]true[/code] and [param to_id] is disabled the search may take an unusually long time to finish.
</description>

View File

@@ -79,7 +79,8 @@
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the IDs of the points that form the path found by AStar2D between the given points. The array is ordered from the starting point to the ending point of the path.
If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]).
If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] When [param allow_partial_path] is [code]true[/code] and [param to_id] is solid the search may take an unusually long time to finish.
</description>
</method>
@@ -97,7 +98,8 @@
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description>
Returns an array with the points that are in the path found by [AStarGrid2D] between the given points. The array is ordered from the starting point to the ending point of the path.
If there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
If [param from_id] point is disabled, returns an empty array (even if [code]from_id == to_id[/code]).
If [param from_id] point is not disabled, there is no valid path to the target, and [param allow_partial_path] is [code]true[/code], returns a path to the point closest to the target that can be reached.
[b]Note:[/b] This method is not thread-safe; it can only be used from a single [Thread] at a given time. Consider using [Mutex] to ensure exclusive access to one thread to avoid race conditions.
Additionally, when [param allow_partial_path] is [code]true[/code] and [param to_id] is solid the search may take an unusually long time to finish.
</description>

View File

@@ -209,6 +209,28 @@ TEST_CASE("[AStar3D] Add/Remove") {
CHECK_FALSE(a.are_points_connected(0, j, true));
}
}
// It's been great work, cheers. \(^ ^)/
}
TEST_CASE("[AStar3D] Path from disabled point is empty") {
AStar3D a;
Vector3 p1(0, 0, 0);
Vector3 p2(0, 1, 0);
a.add_point(1, p1);
a.add_point(2, p2);
a.connect_points(1, 2);
CHECK_EQ(a.get_id_path(1, 1), Vector<int64_t>{ 1 });
CHECK_EQ(a.get_id_path(1, 2), Vector<int64_t>{ 1, 2 });
CHECK_EQ(a.get_point_path(1, 1), Vector<Vector3>{ p1 });
CHECK_EQ(a.get_point_path(1, 2), Vector<Vector3>{ p1, p2 });
a.set_point_disabled(1, true);
CHECK(a.get_id_path(1, 1).is_empty());
CHECK(a.get_id_path(1, 2).is_empty());
CHECK(a.get_point_path(1, 1).is_empty());
CHECK(a.get_point_path(1, 2).is_empty());
}
} // namespace TestAStar