Godot pathfinding / 2026-06-21 / 15 min read
Updated 2026-07-29 · Verified as of Godot 4.7.1 stable checked against the 4.7 docs on 2026-07-29
AStarGrid2D in Godot 4: complete reference
Set up AStarGrid2D correctly in Godot 4.7.1 with practical code and an interactive demo: update(), solids, weights, diagonals, jumping, and partial paths.
AStarGrid2D is simple. The edge cases aren't.
AStarGrid2D is Godot's grid-based A* pathfinder for 2D games. You give it a rectangular cell region, call update(), mark blocked cells as solid, assign terrain costs with weight_scale, and ask for a path in grid coordinates or world positions. It's the right default for tile-based movement. It isn't a replacement for NavigationServer2D, crowd planning, clearance maps, or a production navigation workflow. If the confusion is a NavigationObstacle2D steering around something while the route query stays unchanged, use NavigationObstacle avoidance vs pathfinding in Godot instead.
AStarGrid2D is a small class. You can read the whole API page in ten minutes, and most people do. Then they ship a grid where the path comes back empty, units cut through wall corners, or the route ignores the road and walks through the swamp. None of that is a Godot conspiracy. It's the gap between knowing the method names and knowing which small handful of settings decide the behavior.
So this is the page I'd keep open in a second tab. It stays on API behavior: setup order, update(), solids, weights, diagonal modes, heuristics, jumping, coordinate conversion, and partial paths. If you're asking when to replace AStarGrid2D with a larger navigation layer, use the limitations and alternatives guide instead.
If you have not chosen the representation yet, the 2D grid, graph, or navmesh guide puts AStarGrid2D beside AStar2D and NavigationServer2D before this page goes into API depth.
If something here is wrong or out of date for your Godot version, I want to know. The API has moved across Godot 4.x, and stale navigation advice turns quickly into a debugging session.
Version notes for Godot 4.x
Godot's AStarGrid2D API has stayed recognizable, but a few details changed enough that older tutorials can mislead you.
If you're publishing code meant for current Godot, use region, not size. If you're reading an older tutorial that calls update() after marking obstacles, read the setup section twice.
| Godot version | What matters for AStarGrid2D |
|---|---|
| 4.0 | The docs use size = Vector2i(...) in the basic setup. |
| 4.1+ | region: Rect2i is the preferred grid shape property; size is deprecated. |
| 4.2+ | fill_solid_region() and fill_weight_scale_region() are available in the docs. |
| 4.3+ | cell_shape adds square, isometric-right, and isometric-down cell placement. get_id_path() and get_point_path() support allow_partial_path. |
| 4.6 beta 3+ | A solid from_id returns an empty path. Godot 4.5 and the first two 4.6 betas could still route from a solid start. |
| 4.7 / 4.7.1 | Godot 4.7 is the current stable docs line; 4.7.1 is the current maintenance release as of 2026-07-29. |
The setup that runs - and the order that matters
Here is the smallest setup that works. The order matters:
Apply solids and weights after update()
update() is the line everyone forgets. AStarGrid2D doesn't rebuild itself after shape changes until you ask it to. Set the region, set the cell size, call update(), then query. Forget it and you'll usually get an empty array with no useful gameplay explanation.
But the more expensive mistake is the opposite one: calling update() after you mark obstacles. update() rebuilds the grid and clears point data, including solidity and weight scales. The safe setup pattern is:
For doors, runtime costs, clean-versus-dirty updates, and point-layer replay, use the dedicated runtime AStarGrid2D update guide.
The rule worth memorizing
Grid shape changes need update(); point data is applied after update(). region, cell_size, offset, and cell_shape are shape or placement changes. Solids and weights are point data. If you change the shape later, replay the solids and weights from your own source of truth.
AStarGrid2D API cheat sheet
Use this as the quick reference before the longer explanations below.
| API | What it does | Needs update()? | Gotcha |
|---|---|---|---|
region | Defines the rectangular cell area | Yes | Preferred over deprecated size |
cell_size | Maps cell coordinates to world positions | Yes | Affects get_point_path(), not which cells are chosen |
offset | Shifts returned world positions | Yes | Useful for visual alignment |
cell_shape | Square, isometric-right, or isometric-down point placement | Yes | Present in 4.3+ docs |
set_point_solid() | Blocks or unblocks one cell | No | Call after update(); guard bounds |
fill_solid_region() | Blocks or unblocks a rectangle of cells | No | Present in 4.2+ docs |
set_point_weight_scale() | Sets terrain traversal cost multiplier | No | 0.0 is allowed; negative values are rejected |
fill_weight_scale_region() | Sets weight over a rectangle | No | Present in 4.2+ docs |
diagonal_mode | Controls diagonal neighbors and corner cutting | No | Set before querying; pair with the right heuristic |
jumping_enabled | Enables Jump Point Search | No | Ignores weight scaling when enabled |
get_id_path() | Returns cell coordinates (Array[Vector2i]) | No | Best for tile logic |
get_point_path() | Returns world positions (PackedVector2Array) | No | Docs warn this method is not thread-safe |
allow_partial_path | Returns closest reachable path when possible | No | Solid targets can make the search slow |
is_dirty() | Reports shape changes needing update() | No | Treat a dirty grid as setup failure |
is_in_boundsv() | Checks if a cell is inside region | No | Use before gameplay-driven point writes |
get_point_data_in_region() | Reads point data in a rectangle | No | Useful for debugging overlays |
clear() | Clears grid state | N/A | Resets the grid shape and data |
What AStarGrid2D actually is
AStarGrid2D is the grid-shaped specialization of Godot's A* search. Plain AStar2D makes you add every point and every connection by hand. That's useful when your graph is irregular: waypoints, a road network, a hand-built node mesh. AStarGrid2D skips all of that. You hand it a rectangular region and a cell size, and the grid is the graph: every cell is a point, neighbors are connected for you, and you spend your time marking which cells are solid and which are expensive.
That's the trade. You give up arbitrary graph shapes and get a dense uniform grid with almost no setup. For tile-based games - strategy, roguelikes, tactics, tower defense, most top-down movement - that's usually the shape you want.
What it isn't: it isn't NavigationServer2D. The navigation server works with baked navigation mesh (navmesh) regions and polygons, agent avoidance, and continuous space. AStarGrid2D works in whole cells. Different tool, different problem. See the Godot pathfinding glossary for the short version of the terms.
Region, cell size, offset, and cell shape
Four properties place the grid in your world:
region: Rect2i- the rectangle of cells, in cell coordinates.Rect2i(0, 0, 64, 64)is a 64x64 grid starting at origin. This is the current property to use.size: Vector2i- old shape property from early Godot 4 docs. It still exists, but it's deprecated. Preferregion, especially when your grid doesn't begin at(0, 0).cell_size: Vector2- how big one cell is in world units. This affects the positions returned byget_point_path().offset: Vector2- a world-space shift added to returned point positions. Use it when your visual grid and logical grid need a consistent offset.cell_shape- square, isometric-right, or isometric-down point placement. It affects how positions are placed in the grid; when changed, callupdate()before querying again.
Inspect alignment instead of guessing
get_point_position(id) gives you the world position of a single cell if you need to inspect alignment by hand. If your path starts one tile off, don't guess. Print the cell, print the region, and print the point position.
Solid cells and disabled cells: your obstacles
This is how walls happen:
Solidity doesn't need another update()
A solid cell is disabled for pathfinding. Nothing routes through it.
Three things matter here. First, solidity changes don't need update(). They apply on the next query. Second, update() clears solidity, so apply solids after the grid has been updated. Third, single-cell reads and writes outside the region fail, so guard gameplay-driven IDs with is_in_boundsv(id).
Deciding which cells should be solid is a content rule, not an AStarGrid2D API feature. If those cells come from painted tiles, use TileMapLayer custom data for walkability as the source of truth.
fill_solid_region() is the rectangle version. Use it when you're stamping a wall, clearing a room, or applying tilemap collision data in blocks. If that collision data comes from painted cells, start with build an AStarGrid2D from a TileMapLayer. It's present in the Godot 4.2 docs and current stable docs.
For the part where changing blockers too often starts costing frames, see dynamic blockers without a full rebuild.
Weight scale: making terrain cost something
Solid is binary. A cell is in or out. Weight is the dial in between.
set_point_weight_scale() multiplies the cost of traveling from a neighboring point into this cell:
For safe 1.0+ terrain authoring, complete route-cost verification, Jump Point Search, sub-1.0 heuristic safety, diagonals, and unit-profile grids, continue with weighted terrain pathfinding in Godot.
Point weights can choose the route, but action points and terrain costs belong to the game transaction that quotes, confirms, and refunds it.
The weight floor is 0.0, not 1.0
Default is 1.0. Higher values are more expensive. Values between 0.0 and 1.0 make a cell cheaper than normal. The source guard rejects values below 0.0, so 0.0 itself is allowed. Treat that as a free-to-enter cell: occasionally useful, usually a sign that the cost model needs a second look. In most game code, keep it boring: 1.0 for normal terrain, > 1.0 for mud, water, danger, or rough ground.
Like solidity, weight changes apply after update() and don't need another update call.
One caveat: weights and heuristics talk to each other. For A* to remain optimal, the estimate must not overestimate the real remaining cost. If your weighted routes look strange, check the heuristic pairing before blaming the weight values.
Diagonal modes: corner cutting and how to stop it
diagonal_mode decides whether units can move diagonally and whether they can slip diagonally past obstacles. It has four values:
DIAGONAL_MODE_ALWAYS- diagonals are allowed even when adjacent solid cells would make the move look like corner cutting.DIAGONAL_MODE_NEVER- orthogonal movement only. Four directions.DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE- a diagonal is allowed if at least one of the two side-adjacent cells is walkable.DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES- a diagonal is allowed only when both side-adjacent cells are walkable. This is the usual choice when walls should feel solid.
Diagonal mode doesn't dirty the grid
Changing diagonal_mode doesn't require update() in current source; set it before querying and pair it with a matching heuristic. If you want no diagonals, use DIAGONAL_MODE_NEVER and usually HEURISTIC_MANHATTAN.
Heuristics: compute vs estimate
AStarGrid2D exposes two heuristic settings:
default_compute_heuristic- used for the cost between connected cells if_compute_cost()is not overridden.default_estimate_heuristic- used for the estimate from a point to the goal if_estimate_cost()is not overridden.
Heuristic pairings to start with
Both heuristic settings default to HEURISTIC_EUCLIDEAN. The options are EUCLIDEAN, MANHATTAN, OCTILE, and CHEBYSHEV.
| Movement | Estimate heuristic to start with |
|---|---|
| 4-direction grid | HEURISTIC_MANHATTAN |
| 8-direction grid with diagonal cost | HEURISTIC_OCTILE |
| 8-direction grid where diagonal and straight are treated similarly | HEURISTIC_CHEBYSHEV |
| Unsure / conservative default | HEURISTIC_EUCLIDEAN |
Don't overestimate the real remaining cost
On a square grid with diagonal movement, Manhattan overestimates because it counts diagonal progress as if it needed two orthogonal moves. That can make the search faster-looking and less correct. If your path is almost right but not quite, heuristic plus diagonal mode is the first pair to inspect.
Jumping (JPS): fast path, sharp caveat
jumping_enabled = true switches on Jump Point Search. On a large, open, uniform-cost grid it can reduce how many intermediate points the search expands. It can also change the shape of returned paths because the algorithm jumps over long runs of open cells.
The current docs include the important warning: when jumping is enabled, weight scaling isn't considered in pathfinding. So this isn't a free speed button for weighted terrain.
Use it for open, uniform-cost grids. Turn it off when terrain weights are part of the design.
get_id_path vs get_point_path, and allow_partial_path
Two methods ask for a route:
Both return a path for one point id per step. If that point represents the top-left anchor of a 2x2 or rectangular unit, the movement layer must apply the same footprint convention while executing every returned cell; the multi-tile movement guide covers anchor placement, visual centers, transition checks, and rotation.
get_id_path(from, to)returnsArray[Vector2i]/Vector2i[]of cell coordinates. Use this for tile logic, movement ranges, turn-based steps, and debugging.get_point_path(from, to)returns aPackedVector2Arrayof world positions. Use this to drive a sprite, draw a line, or feed a simple follow loop.
Run one query at a time per grid instance
The Godot 4.7 class reference explicitly marks get_point_path() as not thread-safe and recommends a Mutex when multiple threads can reach the same instance.
Do not assume get_id_path() is safe for concurrent calls just because its method entry lacks the same note. In the 4.7 source, both path methods call the same stateful _solve() pass, which updates shared search bookkeeping on the grid. The cautious production rule is one active path query per AStarGrid2D instance.
If path work must run in parallel, serialize access to a shared instance or prepare a separate grid with the same region, solids, weights, and movement policy for each worker. Do not change grid structure or point data while another thread is querying that instance.
Partial paths aren't a validation substitute
In current Godot 4.x docs, both path methods can take allow_partial_path. With it true, an unreachable goal can return a path to the closest reachable cell instead of an empty array.
Two catches: if the start cell is solid, current docs say the result is empty even when from_id == to_id; this became the current behavior in Godot 4.6 beta 3, so older tutorials may show a path from a solid start. If allow_partial_path is true while the target cell is solid, the search may also take unusually long.
The navigation debug panel includes a small AStarGrid2D helper that verifies this last-cell behavior before mixing it with NavigationServer2D diagnostics.
So don't use partial path as a substitute for validation. Validate bounds and solidity first. Then query.
TileMapLayer alignment: the quiet source of wrong paths
AStarGrid2D uses grid IDs. Gameplay code often starts from world positions. Tile maps add another space in between.
For TileMapLayer, the safe conversion is usually:
Print converted cells before inspecting the solver
If your TileMapLayer is at (0, 0), skipping to_local() may appear to work. Then someone moves the layer under a parent and every path starts lying. Print the converted cells before you inspect the solver.
Reference boundary: API behavior, not replacement strategy
Everything above is the tool working as designed. This page is the API reference you keep nearby while implementing AStarGrid2D: what to call, in what order, and which settings change the path shape.
Performance and replacement questions live in separate proof pages. If several enemies share one player target in a maze, use the maze-chase starter to see when one shared Dijkstra field replaces per-pursuer paths. If many agents request full paths in the same frame, the issue is usually query shape; see moving 10,000 agents in Godot. If one grid must answer queries for several square agent sizes, use Annotated A* with clearance-aware neighbor expansion. Rectangular footprints and circle radii need the separate shape-aware grid-clearance contract. For dynamic blockers, scheduling, or diagnostics, use AStarGrid2D limitations and alternatives in Godot.
Frequently asked questions
What is AStarGrid2D in Godot?
AStarGrid2D is Godot's grid-based A* pathfinder. You give it a rectangular region and a cell size, call update(), then mark cells as solid or weighted. It's built for tile-based 2D movement.
AStarGrid2D vs AStar2D - what is the difference?
AStar2D is a general graph where you add points and connections yourself. AStarGrid2D assumes a rectangular grid and wires the neighboring cells for you. Use AStar2D for irregular waypoint graphs; use AStarGrid2D for tile maps.
AStarGrid2D vs NavigationServer2D - which should I use?
Use AStarGrid2D for whole-cell, tile-based movement where you want per-cell control. Use NavigationServer2D for continuous movement over baked navigation mesh regions, especially when your world isn't naturally a grid.
Does AStarGrid2D.update() clear solid cells and weight scales?
A dirty update() rebuilds the grid and clears point data, including solidity and weight scale. Call it after shape changes, then apply solids and weights again from your own data. The runtime AStarGrid2D update guide covers the clean-update no-op and the complete replay workflow.
Why does my AStarGrid2D path come back empty?
The usual causes are a dirty grid that needs update(), start or goal outside region, a solid start cell, a solid or unreachable goal, wrong world-to-cell conversion, or disconnected walkable areas.
What does get_id_path return?
get_id_path() returns cell coordinates: Array[Vector2i] / Vector2i[]. Use it when gameplay logic cares about tiles.
What does get_point_path return?
get_point_path() returns world positions as a PackedVector2Array, with cell_size, offset, and cell_shape applied. Use it for drawing or simple movement following.
How do I make some terrain cost more?
Use set_point_weight_scale(cell, value). 1.0 is normal, values above 1.0 are more expensive, 0.0 is allowed as a free-to-enter cell, and negative values are rejected. Weight changes don't need update(), but they're cleared by a later update().
Does jumping_enabled ignore weight_scale?
Yes. Current docs state that enabling jumping disables consideration of weight scaling. Use jumping for open uniform-cost grids; turn it off for weighted terrain.
Does AStarGrid2D support isometric or hex grids?
Current Godot has cell_shape for square, CELL_SHAPE_ISOMETRIC_RIGHT, and CELL_SHAPE_ISOMETRIC_DOWN placement. Hex grids aren't a native AStarGrid2D mode; you need your own coordinate mapping/neighbor logic or a different graph approach.
How many agents can AStarGrid2D handle?
The API does not define a universal agent limit. Repeated per-agent path queries in the same frame are the common frame killer; use the measured crowd benchmark for that performance question.