Guards walking their rounds, a robot pacing a corridor, a boat circling a lake: patrol paths make a world feel alive and give stealth and action games something to plan around. This guide builds a Unity enemy patrol with waypoints two ways: simple movement for open areas and 2D games, and a NavMeshAgent version for 3D levels with obstacles. Both support wait times, looping and ping-pong paths, and draw the route in the Scene view so you can edit it visually.
A Reusable Patrol Path
Keep the path separate from the enemy. A path is just an ordered list of points, which several enemies can share. Using child transforms as waypoints lets you move them with the normal Scene view tools.
using UnityEngine;
public class PatrolPath : MonoBehaviour
{
public enum Mode { Loop, PingPong, Once }
public Mode mode = Mode.Loop;
public int Count => transform.childCount;
public Vector3 GetPoint(int i) => transform.GetChild(i).position;
public int Next(int current, ref int direction)
{
if (Count <= 1) return 0;
switch (mode)
{
case Mode.Loop:
return (current + 1) % Count;
case Mode.PingPong:
if (current + direction >= Count || current + direction < 0) direction = -direction;
return current + direction;
default:
return Mathf.Min(current + 1, Count - 1);
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.cyan;
for (int i = 0; i < Count; i++)
{
Gizmos.DrawSphere(GetPoint(i), 0.25f);
if (i + 1 < Count) Gizmos.DrawLine(GetPoint(i), GetPoint(i + 1));
}
if (mode == Mode.Loop && Count > 2) Gizmos.DrawLine(GetPoint(Count - 1), GetPoint(0));
}
}
Create an empty GameObject called PatrolPath_A, add this script, and add empty children named WP0, WP1 and so on. The cyan lines show the route even when nothing is selected. Duplicate a child with Ctrl+D to add a point.
Simple Patrol Without NavMesh
For open ground, flying enemies or simple levels, move straight toward each point:
using UnityEngine;
public class SimplePatrol : MonoBehaviour
{
[SerializeField] PatrolPath path;
[SerializeField] float speed = 2.5f;
[SerializeField] float turnSpeed = 360f;
[SerializeField] float waitTime = 1f;
[SerializeField] float arriveDistance = 0.1f;
int index;
int direction = 1;
float waitUntil;
void Update()
{
if (path == null || path.Count == 0 || Time.time < waitUntil) return;
Vector3 target = path.GetPoint(index);
target.y = transform.position.y; // stay level
Vector3 toTarget = target - transform.position;
if (toTarget.magnitude <= arriveDistance)
{
index = path.Next(index, ref direction);
waitUntil = Time.time + waitTime;
return;
}
Quaternion look = Quaternion.LookRotation(toTarget);
transform.rotation = Quaternion.RotateTowards(transform.rotation, look, turnSpeed * Time.deltaTime);
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
MoveTowards never overshoots, so arrival is reliable even with a tiny arrive distance. RotateTowards turns at a fixed rate, which looks more natural than snapping. This moves the transform directly, so it’s best for enemies without physics or with a kinematic Rigidbody. For physics enemies, use rb.MovePosition in FixedUpdate instead. The Lerp and MoveTowards guide explains the difference.
Patrol With a NavMeshAgent
In 3D levels with walls, stairs and obstacles, let the NavMesh handle pathfinding between waypoints. Bake a NavMesh Surface first; the NavMesh tutorial covers setup in Unity 6.
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class NavPatrol : MonoBehaviour
{
[SerializeField] PatrolPath path;
[SerializeField] float waitTime = 1.5f;
NavMeshAgent agent;
int index;
int direction = 1;
float waitUntil;
bool waiting;
void Awake() => agent = GetComponent<NavMeshAgent>();
void OnEnable()
{
if (path != null && path.Count > 0) GoTo(index);
}
void Update()
{
if (path == null || path.Count == 0) return;
if (waiting)
{
if (Time.time >= waitUntil) { waiting = false; index = path.Next(index, ref direction); GoTo(index); }
return;
}
bool arrived = !agent.pathPending && agent.remainingDistance <= agent.stoppingDistance + 0.05f;
if (arrived)
{
waiting = true;
waitUntil = Time.time + waitTime;
}
}
void GoTo(int i)
{
agent.SetDestination(path.GetPoint(i));
}
}
The pathPending check matters. Right after SetDestination, the path may not be calculated yet and remainingDistance can be 0, which would make the agent think it had already arrived and skip every waypoint. Set the agent’s Stopping Distance to something small like 0.2, and turn off Auto Braking if you want smooth walking through waypoints without slowing down at each one (only when wait time is 0).
Waypoints should sit on the NavMesh. If one is slightly off, the agent goes to the nearest reachable point, which might be on the wrong side of a wall. Use NavMesh.SamplePosition to snap points if needed.
Waiting and Looking Around
A guard that pauses and looks around feels far more alive. Some cheap upgrades:
- Per-waypoint wait times. Add a small
Waypointcomponent with awaitTimefield on each child and read it instead of one global value. - Face a direction while waiting. Rotate toward the waypoint’s forward vector during the wait, so guards look out of windows or down corridors.
- Animation. Drive a Speed parameter from
agent.velocity.magnitudeso the walk and idle animations match movement. - Randomness. Add a random 0 to 1 second to wait times, so multiple guards don’t move in lockstep.
Switching to Chase and Back
Patrol is usually one state in a bigger brain: patrol until the player is spotted, chase, then return to the nearest waypoint. Disable the patrol component when chasing and enable it to resume. In OnEnable, find the closest waypoint index first so the guard doesn’t walk all the way back to where it left off:
int Closest()
{
int best = 0; float bestD = float.MaxValue;
for (int i = 0; i < path.Count; i++)
{
float d = (path.GetPoint(i) - transform.position).sqrMagnitude;
if (d < bestD) { bestD = d; best = i; }
}
return best;
}
For a proper structure with patrol, chase and attack states, see the enemy AI state machine guide.
2D Patrols
The simple patrol works in 2D if you remove the 3D rotation. Use Vector2.MoveTowards and flip the sprite based on direction (spriteRenderer.flipX = toTarget.x < 0). For platformer enemies that walk back and forth on a ledge, a common alternative to waypoints is a raycast down in front of the enemy; when it finds no ground or hits a wall, turn around.
Tips for Good Patrols
- Design patrols so players can learn them. Predictable routes with clear pauses make stealth fair.
- Avoid sharp, tiny loops. Agents with large turn radii overshoot and wobble.
- Keep waypoint counts low. Four to six points with good placement beat twenty cluttered ones.
- Use the path’s gizmos to check routes in the Scene view before playing.
📚 Learn more about game AI
Patrols, perception and state machines are classic game AI topics. A few books on our recommended reading list cover them in friendly detail.
Troubleshooting
Agent skips waypoints instantly
You’re checking remainingDistance before the path is calculated. Check pathPending first.
Enemy circles a waypoint forever
Arrive distance is too small for the speed and turn rate. Increase it, or use MoveTowards which can’t overshoot.
“SetDestination can only be called on an active agent that has been placed on a NavMesh”
The agent isn’t on the NavMesh. Bake the NavMesh and place the enemy on it, or call Warp to snap it on.
Enemy floats or sinks while patrolling
Waypoints are at different heights. Flatten the target’s Y for simple patrols, or use the NavMesh.
Several enemies stuck together
Give them different paths, start indexes or random waits, and set NavMeshAgent avoidance priority.
FAQ
Should I use NavMesh for patrols?
Use it in 3D levels with obstacles. For open areas, flying enemies or 2D, simple movement is lighter and easier to control.
Can enemies share one path?
Yes. The path is separate, so start them at different indexes to spread them out.
How do I make a random patrol?
Pick a random waypoint each time instead of the next one, or pick a random point within a radius on the NavMesh.
Why use child transforms for waypoints?
You can move them with the normal tools, and they move with the path if you reposition the whole thing.
Can I use a spline instead?
Yes. Unity’s Splines package lets objects follow a smooth curve, which suits vehicles and flying enemies.
Reader questions
Asked by readers of this article and answered by AI. Behavior can differ between Unity versions, so test in yours.




Leave a Reply