How to Make a Basic Patrolling System for Monsters and NPCs using waypoints in Unity

Published - Last modified

When playing games, NPCs and monsters are often found moving between one point to the other, which gives a much livelier look to a scene compared to having only static elements. In a previous article, I created a patrolling ennemy using raycasting but this time we’ll be implementing this feature by specifying a travel path, thanks to nodes in a linked list.

Demo Patrolling System
Our waypoint Patrolling System will give us a result similar to this

What is a waypoint

A waypoint is an intermediary point or place in the middle of a route. Let’s say we want our character to patrol from point A to point B, we can define intermediary points C, D, E… to be visited or passed through by the character. This is a cheap way to fit a character’s movement pattern to an environment.

Create a patrolling path using Waypoint Nodes and Linked Lists

We’ll start by creating Waypoints, a MonoBehaviour class that specifies a point which the character must reach in its patrol. This is pretty straightforward, but the trick is that we’ll be implementing a linked list as well: this means that each Waypoint will contain a reference to the next Waypoint to be reached.

The benefits of this approach are obvious: once a character has reached the waypoint (or a close enough distance), it can get the location of the next waypoint and go towards it. A patrolling system means then to go from one waypoint to the next.

Here’s how we can write it, with some convenience methods added:

public class Waypoint : MonoBehaviour
{
    public Waypoint mNextWaypoint;

    private Vector3 _mPosition;


    // Start is called before the first frame update
    void Start()
    {
        // caching the Waypoint's position to avoid accessing the transform everytime
        _mPosition = transform.position;
    }

    // convenience method to quickly get distance between waypoint and character
    public float GetDistance(Vector3 characterPosition)
    {
        return Vector3.Distance(_mPosition, characterPosition);
    }

    // get the direction from the character to waypoint to handle character movement
    public Vector3 GetDirection(Vector3 characterPosition)
    {
        Vector3 heading = _mPosition - characterPosition;
        return heading / heading.magnitude;
    }

    public Waypoint GetNextWaypoint()
    {
        return mNextWaypoint;
    }

    public Vector3 GetPosition()
    {
        return _mPosition;
    }
}

Now we’ll write our Patrolling System. This system is simple: it will move the character’s position towards the current target waypoint at a given speed. Once the distance to the waypoint is below the selected threshold, we get the reference to the next waypoint and start navigating towards it.

public class PatrollingSystem : BaseSystem
{
    public Waypoint mCurrentTargetWaypoint;
    public float mSpeed = 3f;
    public float mDistanceThreshold = 0.2f;

    void Update() {
        // check distance to waypoint compared to threshold
        if (mCurrentTargetWaypoint.GetDistance(transform.position) <= mDistanceThreshold)
        {
            // if close enough, get next waypoint
            mCurrentTargetWaypoint = mCurrentTargetWaypoint.GetNextWaypoint();

        }

        // move towards waypoint
        transform.Translate(
            mCurrentTargetWaypoint.GetDirection() * mSpeed * Time.deltaTime
        );
    }

This is all that’s needed! Now if we set up a character with the PatrollingSystem component, create a few gameobjects with Waypoints and link them up, we can see our navigation is working properly. Congrats!

Potential Issues and Improvements

I wanted to keep this article light, so I’ll quickly mention potential issues and how to solve them.

  1. Our code does not include rotation
    This can easily be fixed by calling transform.LookAt or use a Coroutine for a smooth rotation.

  2. Movement logic should be changed
    Avoid using translate directly on the transform. You might prefer using a CharacterController to handle your movement, and put the handling of the movement in FixedUpdate.

  3. Adding pauses/breaks in the movement
    This is pretty simple, you can add some sort of “paused” boolean property to temporarily stop the movement of the character and/or execute alternative behaviour and resume after some time or once a condition has been met.

  4. Waypoints chain must loop
    Else, the code will crash upon reaching the last node (trying to use null in mathematical operations). You must ensure no node is left without a next node. Alternatively, you can also modify the code to create a doubly linked list (keep track of parent and child) so that you can navigate the chain back up, or even set up branching paths.

That’s all for now! I hope this article will help you create your own AI systems and I hope to be able to show you a quick demonstration video on our brand new Youtube channel!