Merge pull request #74106 from dalexeev/astar-grid-2d-negative-coords
Allow negative coordinates in `AStarGrid2D`
This commit is contained in:
commit
cc0edf810f
3 changed files with 66 additions and 36 deletions
|
@ -32,6 +32,8 @@
|
|||
|
||||
#include "core/variant/typed_array.h"
|
||||
|
||||
#define GET_POINT_UNCHECKED(m_id) points[m_id.y - region.position.y][m_id.x - region.position.x]
|
||||
|
||||
static real_t heuristic_euclidian(const Vector2i &p_from, const Vector2i &p_to) {
|
||||
real_t dx = (real_t)ABS(p_to.x - p_from.x);
|
||||
real_t dy = (real_t)ABS(p_to.y - p_from.y);
|
||||
|
@ -59,16 +61,29 @@ static real_t heuristic_chebyshev(const Vector2i &p_from, const Vector2i &p_to)
|
|||
|
||||
static real_t (*heuristics[AStarGrid2D::HEURISTIC_MAX])(const Vector2i &, const Vector2i &) = { heuristic_euclidian, heuristic_manhattan, heuristic_octile, heuristic_chebyshev };
|
||||
|
||||
void AStarGrid2D::set_region(const Rect2i &p_region) {
|
||||
ERR_FAIL_COND(p_region.size.x < 0 || p_region.size.y < 0);
|
||||
if (p_region != region) {
|
||||
region = p_region;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
Rect2i AStarGrid2D::get_region() const {
|
||||
return region;
|
||||
}
|
||||
|
||||
void AStarGrid2D::set_size(const Size2i &p_size) {
|
||||
WARN_DEPRECATED_MSG(R"(The "size" property is deprecated, use "region" instead.)");
|
||||
ERR_FAIL_COND(p_size.x < 0 || p_size.y < 0);
|
||||
if (p_size != size) {
|
||||
size = p_size;
|
||||
if (p_size != region.size) {
|
||||
region.size = p_size;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
Size2i AStarGrid2D::get_size() const {
|
||||
return size;
|
||||
return region.size;
|
||||
}
|
||||
|
||||
void AStarGrid2D::set_offset(const Vector2 &p_offset) {
|
||||
|
@ -95,9 +110,11 @@ Size2 AStarGrid2D::get_cell_size() const {
|
|||
|
||||
void AStarGrid2D::update() {
|
||||
points.clear();
|
||||
for (int64_t y = 0; y < size.y; y++) {
|
||||
const int64_t end_x = region.position.x + region.size.width;
|
||||
const int64_t end_y = region.position.y + region.size.height;
|
||||
for (int64_t y = region.position.y; y < end_y; y++) {
|
||||
LocalVector<Point> line;
|
||||
for (int64_t x = 0; x < size.x; x++) {
|
||||
for (int64_t x = region.position.x; x < end_x; x++) {
|
||||
line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size));
|
||||
}
|
||||
points.push_back(line);
|
||||
|
@ -106,11 +123,11 @@ void AStarGrid2D::update() {
|
|||
}
|
||||
|
||||
bool AStarGrid2D::is_in_bounds(int p_x, int p_y) const {
|
||||
return p_x >= 0 && p_x < size.width && p_y >= 0 && p_y < size.height;
|
||||
return region.has_point(Vector2i(p_x, p_y));
|
||||
}
|
||||
|
||||
bool AStarGrid2D::is_in_boundsv(const Vector2i &p_id) const {
|
||||
return p_id.x >= 0 && p_id.x < size.width && p_id.y >= 0 && p_id.y < size.height;
|
||||
return region.has_point(p_id);
|
||||
}
|
||||
|
||||
bool AStarGrid2D::is_dirty() const {
|
||||
|
@ -154,27 +171,27 @@ AStarGrid2D::Heuristic AStarGrid2D::get_default_estimate_heuristic() const {
|
|||
|
||||
void AStarGrid2D::set_point_solid(const Vector2i &p_id, bool p_solid) {
|
||||
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
|
||||
ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
|
||||
points[p_id.y][p_id.x].solid = p_solid;
|
||||
ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set if point is disabled. Point %s out of bounds %s.", p_id, region));
|
||||
GET_POINT_UNCHECKED(p_id).solid = p_solid;
|
||||
}
|
||||
|
||||
bool AStarGrid2D::is_point_solid(const Vector2i &p_id) const {
|
||||
ERR_FAIL_COND_V_MSG(dirty, false, "Grid is not initialized. Call the update method.");
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
|
||||
return points[p_id.y][p_id.x].solid;
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), false, vformat("Can't get if point is disabled. Point %s out of bounds %s.", p_id, region));
|
||||
return GET_POINT_UNCHECKED(p_id).solid;
|
||||
}
|
||||
|
||||
void AStarGrid2D::set_point_weight_scale(const Vector2i &p_id, real_t p_weight_scale) {
|
||||
ERR_FAIL_COND_MSG(dirty, "Grid is not initialized. Call the update method.");
|
||||
ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
|
||||
ERR_FAIL_COND_MSG(!is_in_boundsv(p_id), vformat("Can't set point's weight scale. Point %s out of bounds %s.", p_id, region));
|
||||
ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale));
|
||||
points[p_id.y][p_id.x].weight_scale = p_weight_scale;
|
||||
GET_POINT_UNCHECKED(p_id).weight_scale = p_weight_scale;
|
||||
}
|
||||
|
||||
real_t AStarGrid2D::get_point_weight_scale(const Vector2i &p_id) const {
|
||||
ERR_FAIL_COND_V_MSG(dirty, 0, "Grid is not initialized. Call the update method.");
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
|
||||
return points[p_id.y][p_id.x].weight_scale;
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), 0, vformat("Can't get point's weight scale. Point %s out of bounds %s.", p_id, region));
|
||||
return GET_POINT_UNCHECKED(p_id).weight_scale;
|
||||
}
|
||||
|
||||
AStarGrid2D::Point *AStarGrid2D::_jump(Point *p_from, Point *p_to) {
|
||||
|
@ -285,15 +302,15 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
|
|||
bool has_left = false;
|
||||
bool has_right = false;
|
||||
|
||||
if (p_point->id.x - 1 >= 0) {
|
||||
if (p_point->id.x - 1 >= region.position.x) {
|
||||
left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y);
|
||||
has_left = true;
|
||||
}
|
||||
if (p_point->id.x + 1 < size.width) {
|
||||
if (p_point->id.x + 1 < region.position.x + region.size.width) {
|
||||
right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y);
|
||||
has_right = true;
|
||||
}
|
||||
if (p_point->id.y - 1 >= 0) {
|
||||
if (p_point->id.y - 1 >= region.position.y) {
|
||||
top = _get_point_unchecked(p_point->id.x, p_point->id.y - 1);
|
||||
if (has_left) {
|
||||
top_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y - 1);
|
||||
|
@ -302,7 +319,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
|
|||
top_right = _get_point_unchecked(p_point->id.x + 1, p_point->id.y - 1);
|
||||
}
|
||||
}
|
||||
if (p_point->id.y + 1 < size.height) {
|
||||
if (p_point->id.y + 1 < region.position.y + region.size.height) {
|
||||
bottom = _get_point_unchecked(p_point->id.x, p_point->id.y + 1);
|
||||
if (has_left) {
|
||||
bottom_left = _get_point_unchecked(p_point->id.x - 1, p_point->id.y + 1);
|
||||
|
@ -461,19 +478,19 @@ real_t AStarGrid2D::_compute_cost(const Vector2i &p_from_id, const Vector2i &p_t
|
|||
|
||||
void AStarGrid2D::clear() {
|
||||
points.clear();
|
||||
size = Vector2i();
|
||||
region = Rect2i();
|
||||
}
|
||||
|
||||
Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const {
|
||||
ERR_FAIL_COND_V_MSG(dirty, Vector2(), "Grid is not initialized. Call the update method.");
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point out of bounds (%s/%s, %s/%s).", p_id.x, size.width, p_id.y, size.height));
|
||||
return points[p_id.y][p_id.x].pos;
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_id), Vector2(), vformat("Can't get point's position. Point %s out of bounds %s.", p_id, region));
|
||||
return GET_POINT_UNCHECKED(p_id).pos;
|
||||
}
|
||||
|
||||
Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
|
||||
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 out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height));
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height));
|
||||
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);
|
||||
|
@ -520,8 +537,8 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
|
|||
|
||||
TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
|
||||
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 out of bounds (%s/%s, %s/%s)", p_from_id.x, size.width, p_from_id.y, size.height));
|
||||
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point out of bounds (%s/%s, %s/%s)", p_to_id.x, size.width, p_to_id.y, size.height));
|
||||
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);
|
||||
|
@ -565,6 +582,8 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
|
|||
}
|
||||
|
||||
void AStarGrid2D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_region", "region"), &AStarGrid2D::set_region);
|
||||
ClassDB::bind_method(D_METHOD("get_region"), &AStarGrid2D::get_region);
|
||||
ClassDB::bind_method(D_METHOD("set_size", "size"), &AStarGrid2D::set_size);
|
||||
ClassDB::bind_method(D_METHOD("get_size"), &AStarGrid2D::get_size);
|
||||
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &AStarGrid2D::set_offset);
|
||||
|
@ -596,6 +615,7 @@ void AStarGrid2D::_bind_methods() {
|
|||
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
|
||||
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::RECT2I, "region"), "set_region", "get_region");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size");
|
||||
|
@ -617,3 +637,5 @@ void AStarGrid2D::_bind_methods() {
|
|||
BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES);
|
||||
BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX);
|
||||
}
|
||||
|
||||
#undef GET_POINT_UNCHECKED
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
Size2i size;
|
||||
Rect2i region;
|
||||
Vector2 offset;
|
||||
Size2 cell_size = Size2(1, 1);
|
||||
bool dirty = false;
|
||||
|
@ -107,21 +107,21 @@ private:
|
|||
|
||||
private: // Internal routines.
|
||||
_FORCE_INLINE_ bool _is_walkable(int64_t p_x, int64_t p_y) const {
|
||||
if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) {
|
||||
return !points[p_y][p_x].solid;
|
||||
if (region.has_point(Vector2i(p_x, p_y))) {
|
||||
return !points[p_y - region.position.y][p_x - region.position.x].solid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Point *_get_point(int64_t p_x, int64_t p_y) {
|
||||
if (p_x >= 0 && p_y >= 0 && p_x < size.width && p_y < size.height) {
|
||||
return &points[p_y][p_x];
|
||||
if (region.has_point(Vector2i(p_x, p_y))) {
|
||||
return &points[p_y - region.position.y][p_x - region.position.x];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Point *_get_point_unchecked(int64_t p_x, int64_t p_y) {
|
||||
return &points[p_y][p_x];
|
||||
return &points[p_y - region.position.y][p_x - region.position.x];
|
||||
}
|
||||
|
||||
void _get_nbors(Point *p_point, LocalVector<Point *> &r_nbors);
|
||||
|
@ -138,6 +138,9 @@ protected:
|
|||
GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i)
|
||||
|
||||
public:
|
||||
void set_region(const Rect2i &p_region);
|
||||
Rect2i get_region() const;
|
||||
|
||||
void set_size(const Size2i &p_size);
|
||||
Size2i get_size() const;
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
</brief_description>
|
||||
<description>
|
||||
Compared to [AStar2D] you don't need to manually create points or connect them together. It also supports multiple type of heuristics and modes for diagonal movement. This class also provides a jumping mode which is faster to calculate than without it in the [AStar2D] class.
|
||||
In contrast to [AStar2D], you only need set the [member size] of the grid, optionally set the [member cell_size] and then call the [method update] method:
|
||||
In contrast to [AStar2D], you only need set the [member region] of the grid, optionally set the [member cell_size] and then call the [method update] method:
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var astar_grid = AStarGrid2D.new()
|
||||
astar_grid.size = Vector2i(32, 32)
|
||||
astar_grid.region = Rect2i(0, 0, 32, 32)
|
||||
astar_grid.cell_size = Vector2(16, 16)
|
||||
astar_grid.update()
|
||||
print(astar_grid.get_id_path(Vector2i(0, 0), Vector2i(3, 4))) # prints (0, 0), (1, 1), (2, 2), (3, 3), (3, 4)
|
||||
|
@ -49,7 +49,7 @@
|
|||
<method name="clear">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Clears the grid and sets the [member size] to [constant Vector2i.ZERO].
|
||||
Clears the grid and sets the [member region] to [code]Rect2i(0, 0, 0, 0)[/code].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_id_path">
|
||||
|
@ -132,7 +132,8 @@
|
|||
<method name="update">
|
||||
<return type="void" />
|
||||
<description>
|
||||
Updates the internal state of the grid according to the parameters to prepare it to search the path. Needs to be called if parameters like [member size], [member cell_size] or [member offset] are changed. [method is_dirty] will return [code]true[/code] if this is the case and this needs to be called.
|
||||
Updates the internal state of the grid according to the parameters to prepare it to search the path. Needs to be called if parameters like [member region], [member cell_size] or [member offset] are changed. [method is_dirty] will return [code]true[/code] if this is the case and this needs to be called.
|
||||
[b]Note:[/b] All point data (solidity and weight scale) will be cleared.
|
||||
</description>
|
||||
</method>
|
||||
</methods>
|
||||
|
@ -156,8 +157,12 @@
|
|||
<member name="offset" type="Vector2" setter="set_offset" getter="get_offset" default="Vector2(0, 0)">
|
||||
The offset of the grid which will be applied to calculate the resulting point position returned by [method get_point_path]. If changed, [method update] needs to be called before finding the next path.
|
||||
</member>
|
||||
<member name="region" type="Rect2i" setter="set_region" getter="get_region" default="Rect2i(0, 0, 0, 0)">
|
||||
The region of grid cells available for pathfinding. If changed, [method update] needs to be called before finding the next path.
|
||||
</member>
|
||||
<member name="size" type="Vector2i" setter="set_size" getter="get_size" default="Vector2i(0, 0)">
|
||||
The size of the grid (number of cells of size [member cell_size] on each axis). If changed, [method update] needs to be called before finding the next path.
|
||||
[b]Note:[/b] This property is deprecated, use [member region] instead.
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
|
|
Loading…
Reference in a new issue