[Script Library] Baz Navigation Agent Movement

BazNavAgentMovement

Pathfinding AI movement for GameObjects. Powered by Godot’s built-in NavigationAgent2D so enemies route around walls instead of pushing into them. Six behaviors, one node, no manual state code.

DOWNLOAD:
v1.2 (AGM 2.0.0+):
baz_nav_agent_movement.gd (72.3 KB)

  • Follows speed changes while running. The script used to read the speed once when the enemy first started moving. Now it reads it every frame from the object’s movement speed variables (falling back to MoveAndJumpSettings), so changing them with an action or from a script takes effect right away.
  • Changes ease in. With acceleration on, a new speed ramps in using your acceleration and deceleration instead of snapping.
  • The state’s Horizontal Speed Multiplier isn’t read. Change the movement speed variable instead.

v1.1 (AGM 2.0.0+):
baz_nav_agent_movement.gd (70.5 KB)

  • Faces where it’s going on AGMaker 2.0. On 2.0 the Movement facing no longer follows movement that comes from a script, so enemies kept their default facing (always right in side view). The script now turns the object itself.
  • Nothing to change in your setup. It works with the state’s Animation Facing Direction left on Movement, and keeps its last facing while stopped, like during patrol pauses.
  • Upgrading is a file swap. Replace baz_nav_agent_movement.gd with this one. Your nodes and visual scripts stay as they are.

v1.0 (Pre AGM 2.0.0):
baz_nav_agent_movement.gd (68.4 KB)

What it does

Pathfinds your enemy to a target using NavigationAgent2D.

  • Patrol — move between waypoints in Loop or Ping-Pong order
  • Chase — pathfind toward an Object, Group, or named node
  • Flee — pathfind away from a threat using probe directions
  • Random Walk — wander within a radius around the start position
  • Set Direction — move at a constant angle, routing around obstacles
  • Move To Point — one-shot trip to a coordinate, object, group, or named node

Behaviors switch mid-game with one call from any visual script state. Speed, acceleration, and deceleration are read from the entity’s existing MoveAndJumpSettings.

Setting up GameObject navigation nodes

  1. Open your enemy’s GameObject (or Area2DGameObject) scene. Add a child Node2D and attach baz_nav_agent_movement.gd to it.

  2. Add a NavigationAgent2D as a sibling of BazNavAgentMovement under the enemy root. Default settings are fine to start.

  3. Select the BazNavAgentMovement node and pick a Behavior Mode from the inspector dropdown. The inspector shows only the fields relevant to the selected mode. Hover any field for a tooltip.

Behavior Mode What it does
Patrol Cycles a waypoint list in Loop or Ping-Pong order
Chase Pathfinds toward the nearest valid target
Flee Pathfinds away from the nearest threat
Random Walk Picks random destinations within a radius of the start position
Set Direction Moves at a constant angle, navigating around obstacles
Move To Point One-shot trip to a coordinate, node, or group member

Using BazNavAgentMovement in your Visual Script

In the enemy’s visual script, pick your AI state and add an InvokeScript Action:

  • Target Node: BazNavAgentMovement
  • Target Method: apply_movement
  • Invoke Timing: OnStateUpdate

That’s the whole runtime wiring. apply_movement() auto-initializes on first call and runs the selected behavior every physics tick.

Setting up GameScene NavigationRegion2D and NavigationPolygon

  1. Open the game scene where the enemy will live. Add a NavigationRegion2D, assign it a NavigationPolygon resource, and draw the walkable area.

  2. Put any TileMapLayer’s or StaticBody2D’s that you want to count as collisions as children of the NavigationRegion2D and press Bake NavigationPolygon in the editor toolbar. Without a baked navmesh the enemy will not move.

  1. If you need to make the path with more radius around corners, you can adjust the Radius property in the NavigationPolygon resource. You will have to rebake it!

Actions

Method When to call
apply_movement() Every physics tick on the active AI state (OnStateUpdate). Required for basic use.
reset_movement() Stops movement and reinitializes on the next apply_movement() call. Only needed for an explicit reset since movement stops on its own when apply_movement() is no longer called.
switch_to_patrol() OnStateEnter of a patrol state. Restarts the waypoint cycle from waypoint 0.
switch_to_chase() OnStateEnter of a chase state. Acquires the nearest valid target.
switch_to_flee() OnStateEnter of a flee state. Acquires the nearest threat.
switch_to_random_walk() OnStateEnter of a wander state.
switch_to_set_direction() OnStateEnter of a fixed-direction state.
switch_to_move_to_point() OnStateEnter of a one-shot move state. Freezes the destination from the configured source.
return_to_start() Pathfinds back to the spawn position.
return_to_nearest_patrol() Pathfinds to the nearest waypoint and resumes patrol from there.

Conditions

For use in visual script transition condition slots (ScriptConditionEvaluator):

Method True when
is_at_destination() Entity has reached its current target
is_moving() Entity has non-zero velocity
has_completed_patrol() A full Loop or Ping-Pong cycle has finished
is_at_start() Entity is back at its spawn position (within 8 px)
is_returning() A return_to_start or return_to_nearest_patrol trip is in progress
is_patrol_paused() Patrol is active and entity is dwelling at a waypoint
is_patrol_at_last_waypoint() Patrol is active and entity is at or heading to the final waypoint
is_chase_has_target() Chase is active and a valid target is locked

Tips

  • Bake the navmesh first. A missing or unbaked NavigationRegion2D is the most common reason nothing moves. Use Debug > Visible Navigation in the editor to confirm the mesh is where you think it is.
  • Speed comes from MoveAndJumpSettings. It will even follow if acceleration is selected.
  • Debug Enabled prints init, target updates, mode switches, and arrival events to Output. Turn it off for release.
  • Switching modes mid-game doesn’t reset speed. The accel/decel ramp carries over between switches. Call reset_movement() first if you want a hard stop.
  • Set Direction uses carrot-on-a-stick pathfinding. It projects a target 128 px ahead and pathfinds to it, so an enemy moving right will still route around walls without permanently deviating.
  • One node per entity. Two BazNavAgentMovement nodes on the same entity will fight over velocity every frame.
6 Likes

I really need this, but I haven’t been able to get it to work. Could you provide a complete working example? Thank you very much!

Welcome to the forums @xinyn!

If things go smoothly, I will be making a video tutorial on how to use it this week. Until then can you share what is not working and maybe I can help?

I made a video for how to use the script and setup navigation regions (at the very top of this thread). Hopefully it is useful!

This video really helped me! My zombie is now moving as intended, thanks so much! If I run into any other issues later on, I’ll be sure to let you know. Thanks again!

1 Like

I’ve run into another issue: the zombies can now patrol or chase according to my settings, but during these actions, they only face one direction and don’t change their facing direction based on their movement direction :sweat_smile:

I’ve tried many things, but I still haven’t been able to solve this problem…

The only thing I can think of would be causing that is:

  1. The animations are not setup for multiple directions
  2. The states you are moving in are not using Movement as the facing type

If you have already tried those and still cannot get it to work, can you send in your project in a zip to pgmmv-support@gotchagotcha.jp so I could investigate why this would be happening?

Thank you for your reply. I have confirmed that the reasons 1 and 2 you mentioned above are not the issue, so it is likely not caused by those settings.

I noticed that in your demo video Navigation Agent Movement - Action Game Maker Godot, you encountered the same problem: the zombies move as set, but their orientation always faces downward instead of facing the direction of movement.

The zombie animations are setup rather lazily on my part. You can see that the idle animation only has 1 direction (downward) so if you are using the idle animation in that state then it will only face down.


Thank you, Baz. My issue is resolved. Thanks for your patient help~

1 Like

Hi, I’m not sure if you’ve tested the script with version 2.0.4, but I’m encountering the issue described earlier. I have the animations assigned correctly and followed the tutorial—everything works, but the enemy only faces to the right when moving. It’s a side-scrolling game.
When I use the engine’s built-in movement actions, the character’s orientation changes, but it doesn’t happen when using the script.

I will look into this as soon as I can, thanks for bringing it up!

Good news, this one was a 2.0 change. In 2.0 the Movement facing doesn’t follow movement that comes from a script anymore, so the enemy kept its default facing no matter which way it walked.

I updated the first post to v1.1. The script now turns the enemy toward the way it’s moving by itself, so you can leave Animation Facing Direction on Movement. Just swap your baz_nav_agent_movement.gd for the new one, nothing else in your setup changes.

I tested it in a side view project on 2.0.4 and it turns both ways now. Let me know if it still gives you trouble.

2 Likes

One thing: you cannot change an object’s speed while the script is running—neither by modifying it within an action nor via a command.

Just uploaded v1.2 to fix that. The script was only reading the speed once when the enemy first started moving, so it ignored any change after that. Now it reads the speed every frame from the object’s movement speed variables, so changing them while it’s running works.

The one thing it still doesn’t pick up is the Horizontal Speed Multiplier in the state settings, so I’d change the movement speed variable instead.