add partial path return option for astar

* AStar2D, AStar3D and AStarGrid2D now can return a partial path if the destination point isn't reachable but still in the map. This option is available for both get_point_path and get_id_path
This commit is contained in:
ashley 2024-02-06 20:20:13 -08:00 committed by allison
parent f47f4a02c8
commit aa1bbe1542
10 changed files with 247 additions and 24 deletions

View File

@ -0,0 +1,59 @@
/**************************************************************************/
/* a_star.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
Vector<int64_t> AStar3D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
return get_id_path(p_from_id, p_to_id, false);
}
Vector<Vector3> AStar3D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
return get_point_path(p_from_id, p_to_id, false);
}
void AStar3D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::_get_id_path_bind_compat_88047);
ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::_get_point_path_bind_compat_88047);
}
Vector<int64_t> AStar2D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
return get_id_path(p_from_id, p_to_id, false);
}
Vector<Vector2> AStar2D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
return get_point_path(p_from_id, p_to_id, false);
}
void AStar2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::_get_id_path_bind_compat_88047);
ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::_get_point_path_bind_compat_88047);
}
#endif // DISABLE_DEPRECATED

View File

@ -29,6 +29,7 @@
/**************************************************************************/ /**************************************************************************/
#include "a_star.h" #include "a_star.h"
#include "a_star.compat.inc"
#include "core/math/geometry_3d.h" #include "core/math/geometry_3d.h"
#include "core/object/script_language.h" #include "core/object/script_language.h"
@ -319,6 +320,7 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const {
} }
bool AStar3D::_solve(Point *begin_point, Point *end_point) { bool AStar3D::_solve(Point *begin_point, Point *end_point) {
last_closest_point = nullptr;
pass++; pass++;
if (!end_point->enabled) { if (!end_point->enabled) {
@ -332,11 +334,18 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) {
begin_point->g_score = 0; begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); 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); open_list.push_back(begin_point);
while (!open_list.is_empty()) { while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point. Point *p = open_list[0]; // The currently processed point.
// Find point closer to end_point, or same distance to end_point but closer to begin_point.
if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) {
last_closest_point = p;
}
if (p == end_point) { if (p == end_point) {
found_route = true; found_route = true;
break; break;
@ -368,6 +377,8 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) {
e->prev_point = p; e->prev_point = p;
e->g_score = tentative_g_score; 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, end_point->id);
e->abs_g_score = tentative_g_score;
e->abs_f_score = e->f_score - e->g_score;
if (new_point) { // The position of the new points is already known. if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
@ -414,7 +425,7 @@ real_t AStar3D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return from_point->pos.distance_to(to_point->pos); return from_point->pos.distance_to(to_point->pos);
} }
Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) { Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
Point *a = nullptr; Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a); bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id)); ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
@ -434,7 +445,12 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point); bool found_route = _solve(begin_point, end_point);
if (!found_route) { if (!found_route) {
return Vector<Vector3>(); if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<Vector3>();
}
// Use closest point instead.
end_point = last_closest_point;
} }
Point *p = end_point; Point *p = end_point;
@ -463,7 +479,7 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
return path; return path;
} }
Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) { Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
Point *a = nullptr; Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a); bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id)); ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
@ -483,7 +499,12 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point); bool found_route = _solve(begin_point, end_point);
if (!found_route) { if (!found_route) {
return Vector<int64_t>(); if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<int64_t>();
}
// Use closest point instead.
end_point = last_closest_point;
} }
Point *p = end_point; Point *p = end_point;
@ -555,8 +576,8 @@ void AStar3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar3D::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar3D::get_closest_point, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar3D::get_closest_position_in_segment); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar3D::get_closest_position_in_segment);
ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::get_point_path); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_point_path, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::get_id_path); ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
@ -688,7 +709,7 @@ real_t AStar2D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return from_point->pos.distance_to(to_point->pos); return from_point->pos.distance_to(to_point->pos);
} }
Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) { Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
AStar3D::Point *a = nullptr; AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a); bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id)); ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
@ -707,7 +728,12 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point); bool found_route = _solve(begin_point, end_point);
if (!found_route) { if (!found_route) {
return Vector<Vector2>(); if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
return Vector<Vector2>();
}
// Use closest point instead.
end_point = astar.last_closest_point;
} }
AStar3D::Point *p = end_point; AStar3D::Point *p = end_point;
@ -736,7 +762,7 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
return path; return path;
} }
Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) { Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
AStar3D::Point *a = nullptr; AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a); bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id)); ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
@ -756,7 +782,12 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point); bool found_route = _solve(begin_point, end_point);
if (!found_route) { if (!found_route) {
return Vector<int64_t>(); if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
return Vector<int64_t>();
}
// Use closest point instead.
end_point = astar.last_closest_point;
} }
AStar3D::Point *p = end_point; AStar3D::Point *p = end_point;
@ -786,6 +817,7 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
} }
bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) { bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
astar.last_closest_point = nullptr;
astar.pass++; astar.pass++;
if (!end_point->enabled) { if (!end_point->enabled) {
@ -799,11 +831,18 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
begin_point->g_score = 0; begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); 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); open_list.push_back(begin_point);
while (!open_list.is_empty()) { while (!open_list.is_empty()) {
AStar3D::Point *p = open_list[0]; // The currently processed point. AStar3D::Point *p = open_list[0]; // The currently processed point.
// Find point closer to end_point, or same distance to end_point but closer to begin_point.
if (astar.last_closest_point == nullptr || astar.last_closest_point->abs_f_score > p->abs_f_score || (astar.last_closest_point->abs_f_score >= p->abs_f_score && astar.last_closest_point->abs_g_score > p->abs_g_score)) {
astar.last_closest_point = p;
}
if (p == end_point) { if (p == end_point) {
found_route = true; found_route = true;
break; break;
@ -835,6 +874,8 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
e->prev_point = p; e->prev_point = p;
e->g_score = tentative_g_score; 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, end_point->id);
e->abs_g_score = tentative_g_score;
e->abs_f_score = e->f_score - e->g_score;
if (new_point) { // The position of the new points is already known. if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
@ -874,8 +915,8 @@ void AStar2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment);
ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_point_path, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path); ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")

View File

@ -60,6 +60,10 @@ class AStar3D : public RefCounted {
real_t f_score = 0; real_t f_score = 0;
uint64_t open_pass = 0; uint64_t open_pass = 0;
uint64_t closed_pass = 0; uint64_t closed_pass = 0;
// Used for getting closest_point_of_last_pathing_call.
real_t abs_g_score = 0;
real_t abs_f_score = 0;
}; };
struct SortPoints { struct SortPoints {
@ -109,6 +113,7 @@ class AStar3D : public RefCounted {
OAHashMap<int64_t, Point *> points; OAHashMap<int64_t, Point *> points;
HashSet<Segment, Segment> segments; HashSet<Segment, Segment> segments;
Point *last_closest_point = nullptr;
bool _solve(Point *begin_point, Point *end_point); bool _solve(Point *begin_point, Point *end_point);
@ -121,6 +126,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t) GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t) GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
#ifndef DISABLE_DEPRECATED
Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
Vector<Vector3> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
static void _bind_compatibility_methods();
#endif
public: public:
int64_t get_available_point_id() const; int64_t get_available_point_id() const;
@ -149,8 +160,8 @@ public:
int64_t get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const; int64_t get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const;
Vector3 get_closest_position_in_segment(const Vector3 &p_point) const; Vector3 get_closest_position_in_segment(const Vector3 &p_point) const;
Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id); Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id); Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
AStar3D() {} AStar3D() {}
~AStar3D(); ~AStar3D();
@ -171,6 +182,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t) GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t) GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
#ifndef DISABLE_DEPRECATED
Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
Vector<Vector2> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
static void _bind_compatibility_methods();
#endif
public: public:
int64_t get_available_point_id() const; int64_t get_available_point_id() const;
@ -199,8 +216,8 @@ public:
int64_t get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const; int64_t get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const;
Vector2 get_closest_position_in_segment(const Vector2 &p_point) const; Vector2 get_closest_position_in_segment(const Vector2 &p_point) const;
Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id); Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id); Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
AStar2D() {} AStar2D() {}
~AStar2D() {} ~AStar2D() {}

View File

@ -0,0 +1,48 @@
/**************************************************************************/
/* a_star_grid_2d.compat.inc */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
#ifndef DISABLE_DEPRECATED
#include "core/variant/typed_array.h"
TypedArray<Vector2i> AStarGrid2D::_get_id_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
return get_id_path(p_from_id, p_to_id, false);
}
Vector<Vector2> AStarGrid2D::_get_point_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
return get_point_path(p_from_id, p_to_id, false);
}
void AStarGrid2D::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::_get_id_path_bind_compat_88047);
ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::_get_point_path_bind_compat_88047);
}
#endif // DISABLE_DEPRECATED

View File

@ -29,6 +29,7 @@
/**************************************************************************/ /**************************************************************************/
#include "a_star_grid_2d.h" #include "a_star_grid_2d.h"
#include "a_star_grid_2d.compat.inc"
#include "core/variant/typed_array.h" #include "core/variant/typed_array.h"
@ -446,6 +447,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
} }
bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
last_closest_point = nullptr;
pass++; pass++;
if (p_end_point->solid) { if (p_end_point->solid) {
@ -459,12 +461,19 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
p_begin_point->g_score = 0; p_begin_point->g_score = 0;
p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id); 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); open_list.push_back(p_begin_point);
end = p_end_point; end = p_end_point;
while (!open_list.is_empty()) { while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point. Point *p = open_list[0]; // The currently processed point.
// Find point closer to end_point, or same distance to end_point but closer to begin_point.
if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) {
last_closest_point = p;
}
if (p == p_end_point) { if (p == p_end_point) {
found_route = true; found_route = true;
break; break;
@ -508,6 +517,9 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
e->g_score = tentative_g_score; e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, p_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;
if (new_point) { // The position of the new points is already known. if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
} else { } else {
@ -546,7 +558,7 @@ Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const {
return _get_point_unchecked(p_id)->pos; return _get_point_unchecked(p_id)->pos;
} }
Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) {
ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method.");
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_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)); 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));
@ -565,7 +577,12 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
bool found_route = _solve(begin_point, end_point); bool found_route = _solve(begin_point, end_point);
if (!found_route) { if (!found_route) {
return Vector<Vector2>(); if (!p_allow_partial_path || last_closest_point == nullptr) {
return Vector<Vector2>();
}
// Use closest point instead.
end_point = last_closest_point;
} }
Point *p = end_point; Point *p = end_point;
@ -594,7 +611,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
return path; return path;
} }
TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) {
ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method."); ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method.");
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_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)); 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));
@ -613,7 +630,12 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
bool found_route = _solve(begin_point, end_point); bool found_route = _solve(begin_point, end_point);
if (!found_route) { if (!found_route) {
return TypedArray<Vector2i>(); if (!p_allow_partial_path || last_closest_point == nullptr) {
return TypedArray<Vector2i>();
}
// Use closest point instead.
end_point = last_closest_point;
} }
Point *p = end_point; Point *p = end_point;
@ -672,8 +694,8 @@ void AStarGrid2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear);
ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position);
ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::get_point_path); ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_point_path, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::get_id_path); ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")

View File

@ -89,6 +89,10 @@ private:
uint64_t open_pass = 0; uint64_t open_pass = 0;
uint64_t closed_pass = 0; uint64_t closed_pass = 0;
// Used for getting last_closest_point.
real_t abs_g_score = 0;
real_t abs_f_score = 0;
Point() {} Point() {}
Point(const Vector2i &p_id, const Vector2 &p_pos) : Point(const Vector2i &p_id, const Vector2 &p_pos) :
@ -109,6 +113,7 @@ private:
LocalVector<LocalVector<Point>> points; LocalVector<LocalVector<Point>> points;
Point *end = nullptr; Point *end = nullptr;
Point *last_closest_point = nullptr;
uint64_t pass = 1; uint64_t pass = 1;
@ -152,6 +157,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, Vector2i, Vector2i) GDVIRTUAL2RC(real_t, _estimate_cost, Vector2i, Vector2i)
GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i) GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i)
#ifndef DISABLE_DEPRECATED
TypedArray<Vector2i> _get_id_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
Vector<Vector2> _get_point_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
static void _bind_compatibility_methods();
#endif
public: public:
void set_region(const Rect2i &p_region); void set_region(const Rect2i &p_region);
Rect2i get_region() const; Rect2i get_region() const;
@ -198,8 +209,8 @@ public:
void clear(); void clear();
Vector2 get_point_position(const Vector2i &p_id) const; Vector2 get_point_position(const Vector2i &p_id) const;
Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to); Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to); TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
}; };
VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode); VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode);

View File

@ -139,8 +139,10 @@
<return type="PackedInt64Array" /> <return type="PackedInt64Array" />
<param index="0" name="from_id" type="int" /> <param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" /> <param index="1" name="to_id" type="int" />
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description> <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. 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.
[codeblocks] [codeblocks]
[gdscript] [gdscript]
var astar = AStar2D.new() var astar = AStar2D.new()
@ -228,8 +230,10 @@
<return type="PackedVector2Array" /> <return type="PackedVector2Array" />
<param index="0" name="from_id" type="int" /> <param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" /> <param index="1" name="to_id" type="int" />
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description> <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. 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.
[b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector2Array] and will print an error message. [b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector2Array] and will print an error message.
</description> </description>
</method> </method>

View File

@ -168,8 +168,10 @@
<return type="PackedInt64Array" /> <return type="PackedInt64Array" />
<param index="0" name="from_id" type="int" /> <param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" /> <param index="1" name="to_id" type="int" />
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description> <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. 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.
[codeblocks] [codeblocks]
[gdscript] [gdscript]
var astar = AStar3D.new() var astar = AStar3D.new()
@ -255,8 +257,10 @@
<return type="PackedVector3Array" /> <return type="PackedVector3Array" />
<param index="0" name="from_id" type="int" /> <param index="0" name="from_id" type="int" />
<param index="1" name="to_id" type="int" /> <param index="1" name="to_id" type="int" />
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description> <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. 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.
[b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message. [b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message.
</description> </description>
</method> </method>

View File

@ -75,16 +75,20 @@
<return type="Vector2i[]" /> <return type="Vector2i[]" />
<param index="0" name="from_id" type="Vector2i" /> <param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" /> <param index="1" name="to_id" type="Vector2i" />
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description> <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. 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.
</description> </description>
</method> </method>
<method name="get_point_path"> <method name="get_point_path">
<return type="PackedVector2Array" /> <return type="PackedVector2Array" />
<param index="0" name="from_id" type="Vector2i" /> <param index="0" name="from_id" type="Vector2i" />
<param index="1" name="to_id" type="Vector2i" /> <param index="1" name="to_id" type="Vector2i" />
<param index="2" name="allow_partial_path" type="bool" default="false" />
<description> <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. 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.
[b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message. [b]Note:[/b] This method is not thread-safe. If called from a [Thread], it will return an empty [PackedVector3Array] and will print an error message.
</description> </description>
</method> </method>

View File

@ -255,3 +255,16 @@ GH-89992
Validate extension JSON: Error: Field 'classes/Node/methods/replace_by/arguments': size changed value in new API, from 2 to 3. Validate extension JSON: Error: Field 'classes/Node/methods/replace_by/arguments': size changed value in new API, from 2 to 3.
Added optional argument to prevent children to be reparented during replace_by. Compatibility method registered. Added optional argument to prevent children to be reparented during replace_by. Compatibility method registered.
GH-88047
--------
Validate extension JSON: Error: Field 'classes/AStar2D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStar2D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStar3D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStar3D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_id_path/arguments': size changed value in new API, from 2 to 3.
Validate extension JSON: Error: Field 'classes/AStarGrid2D/methods/get_point_path/arguments': size changed value in new API, from 2 to 3.
Added optional "allow_partial_path" argument to get_id_path and get_point_path methods in AStar classes.
Compatibility methods registered.