Godot pathfinding / 2026-07-13 / 13 min read
Verified as of Godot 4.7 stable
Turn order and movement planning in Godot
Build deterministic tactical turns in Godot with explicit phases, movement intents, initiative tie-breakers, conflict resolution, and safe state commits.

The direct answer
How do I manage turn order and queued movement in a Godot tactical RPG? Put the order in an explicit game-state controller, not in _process() or scene-tree order. Snapshot the round queue, collect movement intents without mutating the board, sort every batch with complete tie-breakers, validate each intent when it resolves, commit the winner atomically, and advance the phase only when its visual playback has finished.
AStarGrid2D can propose a path. It does not know whose turn it is, whether two plans compete for one tile, how much AP the move spends, or whether the animation has finished. Those decisions belong to the tactical game-state layer.
You are probably here because
Those failures look unrelated when they live behind separate booleans, signals, and await calls. They are usually different leaks in the same turn contract.
| Your symptom | Go to |
|---|---|
| Input is accepted in the middle of the enemy turn | Give the battle explicit phases |
| A unit acts twice, or a defeated unit still gets a turn | Handle queue invalidation explicitly |
| Animation and state drift apart; skipping playback causes chaos | Keep simulation and animation separate |
| Two planned units end on the same tile | Revalidate when the plan resolves and use the reservation guide |
| Equal-initiative units change order "randomly" | Use a total ordering rule |
Choose the turn model before the queue
"Turn-based" describes several different contracts. Pick one before writing a turn manager.
The complete example supports plan-then-resolve with initiative. A reduced sequential loop appears below because most existing tactical prototypes do not need a batch resolver yet.
For named examples of how faction phases, visible enemy intents, and sequential activations change the surrounding movement contract, compare Fire Emblem, Into the Breach, and XCOM movement in Godot.
| Your game | Movement contract |
|---|---|
| Sequential initiative: one unit acts at a time | Build a round queue, expose one active unit, then validate and commit its command before advancing. |
| Faction phase: one side may use its units in any order | Track the active faction and a set of units that may still act; initiative within that phase is optional. |
| Plan then resolve: several units submit orders first | Store immutable intents, sort the resolution batch, then validate and commit in that deterministic order. |
| Timeline or ATB: actors become ready at different ticks | Order by the next action tick and keep speed or timeline updates as a separate scheduling system. |
Scene-tree order is not initiative
Godot documents that processing usually follows tree order. A lower process_priority runs first, and equal priorities fall back to the position in the scene tree.
Callback order is a scheduling detail. Initiative is a game rule. Reparenting a unit, instancing a scene in a different order, or changing a priority for rendering reasons should not change who acts first in combat.
The turn director should own plain actor identifiers and explicit ordering data. Units can still be Nodes; their position in the tree is not the initiative list.
Give the battle explicit phases
A minimal phase machine keeps input, simulation, and animation from racing each other.
Every core snippet below is a fragment of the same turn_order_movement_planner.gd. The complete assembled file, including the small playback adapter, is in the verified download.
Only PLANNING accepts new movement intents. Only RESOLVING may mutate board state. ANIMATING plays results that have already been decided. ROUND_END applies end-of-round effects and prepares the next snapshot.
This is more useful than a loose collection of booleans such as is_moving, waiting_for_ai, and turn_over. One enum gives every command one legal entry point.
Retrofit the system you already have
You do not need to replace the whole battle loop at once. Move it across the boundary in three shippable steps:
Each step improves one failure class without requiring the later steps. The phase enum is useful even if the game remains strictly sequential.
- Make order data explicit. Keep the current buttons and movement code, but replace scene-tree or callback order with an initiative row and stable unit id.
- Wrap existing gates in one phase enum. Map
is_moving,turn_over, andwaiting_for_aionto legal phase transitions. Delete each boolean only after every caller uses the phase. - Turn direct movement calls into intents. Let the old resolver consume the intent first. Add batch ordering and reservation conflicts only when the game actually allows several pending plans.
The reduced loop for sequential turns
If exactly one unit is active, resolve its intent immediately. There is no batch sort and no contested destination between simultaneous plans; the active-unit check is the ownership rule.
After the accepted move finishes playback, advance _active_unit_id to the next valid queue entry and reopen PLANNING. If the move is rejected, keep the same unit active so the player or AI can choose again.
Snapshot initiative for the round
Build the round order from current actor data, then decide when changes become visible. A simple rule is: speed and status changes affect the next round unless an ability explicitly requeues an actor.
The snapshot prevents a mid-round buff from silently moving a unit that has already acted. Games that deliberately support initiative manipulation can do so through named queue operations instead of a continuously resorted array.
Store a movement intent, not a bound side effect
A plan should describe the requested action. It should not already move the unit, spend AP, or claim cells.
Data-shaped intents are inspectable in a debugger, easy to include in a receipt, and suitable for save or replay work later. A queue of bound Callable values can execute behavior, but it hides the data needed for validation and useful failure reports.
Each plan owns a copied path so later UI edits cannot mutate the queued command by aliasing the same Array.
Planning must not mutate the board
Planning may calculate a range, ask AStarGrid2D for a candidate path, quote AP, and show a reservation warning. It must not change occupancy, AP, or unit position.
If the game lets a player revise an order, replace that unit's plan explicitly. Do not accumulate two active plans and hope the resolver guesses which one is newer.
Use a total ordering rule
Godot's Array.sort_custom() is not stable. If the comparator says two intents are equal, their order may change. Initiative therefore needs secondary and final keys.
The exact policy is a game-design choice. The requirement is that the comparison ends in a unique, stable key. Never use scene-tree position, dictionary iteration order, network arrival timing, or a random comparator as an accidental final tie-breaker.
Revalidate when the plan resolves
A plan is a proposal, not a pre-confirmed move. Earlier winners may occupy its target or change the board before it resolves.
_resolve_one() should skip missing or defeated units, recalculate the final quote against current state, ask the reservation layer for the destination, and commit AP, occupancy, and position atomically. A loser returns a named reason without mutation.
The action-points and terrain-cost transaction owns the quote and confirm details. The turn reservation guide owns contested destination cells.
Keep simulation and animation separate
The resolver decides the authoritative result first. Animation presents that result. The next planning phase should not open until the current result batch has finished playing.
The await calls belong in the playback bridge, after resolution has produced authoritative results:
Pass Callable(animation_runner, "play_move") here. That callback is where an existing await tween.finished or await unit.move_done chain fits. The resolver has already committed the cell; the awaited code presents that result and gates the next phase. Skipping or accelerating playback therefore leaves the same authoritative state.
Call play_results() with await from the battle scene because the function is a coroutine, and provide an asynchronous play_move callback. Calling it without await returns coroutine state instead of the final result Dictionary.
For multi-cell bodies, the winning intent still needs the footprint and transition rules from multi-tile movement in Godot.
Handle queue invalidation explicitly
A unit can be defeated, removed, stunned, or moved before its planned action. The resolver should return a named result and continue; it should not erase items from the array while iterating.
Named results make the turn log useful to UI, tests, replays, and bug reports. "The command did nothing" is not a result contract.
UNIT_MISSINGUNIT_DEFEATEDUNIT_ALREADY_ACTEDPATH_INVALIDTARGET_RESERVEDINSUFFICIENT_APCOMMITTED
Where pathfinding and AI fit
The movement planner consumes paths; it does not replace pathfinding. Use the AStarGrid2D complete reference for the grid query itself and the tactical movement-range guide for the set of legal endpoints.
AI also remains upstream. An influence map can score candidate destinations, but the selected result becomes the same movement intent used by a player. Player input and AI should produce commands through one validation path.
Common failures
Using _process() order as initiative. Engine callback scheduling is not the combat rule.
Sorting only by initiative. sort_custom() is not stable; equal values need explicit tie-breakers.
Mutating during planning. A preview that spends AP or claims occupancy makes cancellation and batch resolution unreliable.
Treating a plan as guaranteed. Revalidate it when it resolves against the current board.
Advancing before animation completes. The UI begins accepting the next turn while the previous result is still playing.
Waiting for animation to change state. Skipped or interrupted visuals then corrupt the simulation.
Deleting queue entries while iterating. Return a skipped result and finish the stable batch instead.
Making the turn director global by default. Keep it battle-scoped unless state truly has to survive scene changes.
Run the turn-planning proof yourself
The Godot 4.7 source package contains the assembled turn planner, the composed reservation helper, a runnable contested-move scene, and its headless verifier.
The machine-readable receipt records 15/15 passed checks, including isolated plan copies, registration-order independence, one deterministic contested-destination winner, playback gating, scene smoke, and a clean extracted-package rerun.
The ZIP is 13,587 bytes with SHA-256 75bfefc611988aa47376e674ba5b1e40cb3365864c27594edca30b94ab364f23. Measurement-gap: this is a correctness receipt for ordering and discrete state transitions, not a runtime, throughput, memory, path-optimality, or released-product benchmark.
Frequently asked questions
How do I manage turn order and queued movement in a Godot tactical RPG?
Use a battle-scoped turn director with explicit phases. Collect data-shaped movement intents without mutation, sort them with initiative plus complete tie-breakers, validate and commit each result through the reservation and AP layers, then advance only after visual playback completes.
Can I use process_priority for initiative in Godot?
No. process_priority controls engine callback order. Initiative is gameplay state and should remain unchanged when a node is reparented or its processing setup changes.
Should initiative be recalculated every turn or once per round?
Either can be a valid game rule, but write it down. A predictable baseline is to snapshot initiative at round start and apply ordinary speed changes next round; abilities that manipulate the current queue should use explicit requeue actions.
Should the turn director be an Autoload?
Usually not. A node owned by the battle scene keeps its state and lifetime clear. Use an Autoload only when the same turn state intentionally survives scene changes or coordinates several loaded scenes.
What order do queued moves resolve in?
Sort them with a complete deterministic rule such as initiative, command sequence, then stable unit id. This article owns that ordering. The destination claim itself belongs to the reservation table, which accepts the first valid winner and rejects the loser without mutation.
Should game state update before or after the movement animation?
Commit authoritative state before playback, then keep the director in an ANIMATING phase until the visual sequence acknowledges completion. Animation presents the result; it does not choose it.