NPC NavMeshAgent to follow the waypoints in a sequence

Waypoints are often used as a guide to make autonomously moving NPCs and enemies follow a path in a general way (but be able to respond with other directional behaviors, such as flee or seek, if friends/predators/prey are sensed nearby). The waypoints are arranged in a sequence, so that when the character reaches, or gets close to a waypoint, it will then select the next waypoint in the sequence as the target location to move towards. This recipe demonstrates an arrow object moving towards a waypoint, and then, when it gets close enough, it will choose the next waypoint in the sequence as the new target destination. When the last waypoint has been reached, it again starts heading towards the first waypoint.

Since Unity's NavMeshAgent has simplified coding NPC behavior, our work in this recipe becomes basically finding the position of the next waypoint, and then telling the NavMeshAgent that this waypoint is its new destination.

NPC NavMeshAgent to follow the waypoints in a sequence

Getting ready

This recipe builds upon the player-controlled 3D cube Unity project that you created at the beginning of this chapter. So, make a copy of this project, open it, and then follow the steps for this recipe.

For this recipe, we have prepared the yellow brick texture image that you need in a folder named Textures in the 1362_08_06 folder.

How to do it...

To instruct an object to follow a sequence of waypoints, follow these steps:

  1. Delete the Cube-player GameObject, since we are going to be creating an NPC computer controlled agent.
  2. Create a sphere named Sphere-arrow, position at (2, 0.5, 2), and scale it as (1,1,1).
  3. Create a second sphere named Sphere-small, and scale it as (0.5, 0.5, 0.5).
  4. Child Sphere-small to Sphere-arrow, and then position it at (0, 0, 0.5).
  5. In the Inspector, add a new NavMeshAgent to Sphere-arrow, and then choose Add Component | Navigation | NavMeshAgent.
  6. Set the Stopping Distance property of the NavMeshAgent component to 2.
  7. Display the Navigation panel by choosing Window | Navigation.
  8. Click on the Bake button at the bottom of the Navigation panel. When the Navigation panel is displayed, you'll see a blue tint on the parts of the Scene panel that are walkable, which will be all parts of the terrain, except near the edges.
  9. Add an instance of the following C# script class called ArrowNPCMovement to the Sphere-arrow GameObject:
    using UnityEngine;
    using System.Collections;
    
    public class ArrowNPCMovement : MonoBehaviour {
      private GameObject targetGO = null;
      private WaypointManager waypointManager;
      private NavMeshAgent navMeshAgent;
    
      void Start (){
        navMeshAgent = GetComponent<NavMeshAgent>();
        waypointManager = GetComponent<WaypointManager>();
        HeadForNextWayPoint();
      }
    
      void Update (){
        float closeToDestinaton = navMeshAgent.stoppingDistance * 2;
        if (navMeshAgent.remainingDistance < closeToDestinaton){
          HeadForNextWayPoint ();
        }
      }
    
      private void HeadForNextWayPoint (){
        targetGO = waypointManager.NextWaypoint (targetGO);
        navMeshAgent.SetDestination (targetGO.transform.position);
      }
    }
  10. Create a new capsule object named Capsule-waypoint-0 at (-12, 0, 8), and give it the waypoint tag.
  11. Copy Capsule-waypoint -0, name the copy as Capsule-waypoint -3, and position this copy at (8, 0, -8).

    Note

    We are going to add some intermediate waypoints numbered 1 and 2 later on. This is why our second waypoint here is numbered 3, in case you were wondering.

  12. Add the following C# script class called WaypointManager to the Sphere-arrow GameObject:
    using UnityEngine;
    
    public class WaypointManager : MonoBehaviour {
      public GameObject wayPoint0;
      public GameObject wayPoint3;
    
      public GameObject NextWaypoint(GameObject current){
        if(current == wayPoint0)
          return wayPoint3;
        else
          return wayPoint0;
      }
    }
  13. Ensure that Sphere-arrow is selected in the Inspector for the WaypointManager scripted component. Drag Capsule-waypoint-0 and Capsule-waypoint-3 over the public variable projectile called Way Point 0 and Way Point 3, respectively.
    How to do it...
  14. Display the Navigation panel by choosing Window | Navigation.
  15. Click on the Bake button at the bottom of the Navigation panel. When the Navigation panel is displayed, you'll see a blue tint on the parts of the Scene that are walkable, which will be all the parts of the terrain, except near the edges.
  16. Now, run your game. The arrow object will first move towards one of the waypoint capsules, then when it gets close to it, it will slow down, turn around, head towards the other waypoint capsule, and keep doing that continuously.

How it works...

The NavMeshAgent component that we added to the Sphere-arrow GameObject does most of the work for us. NavMeshAgents need two things: a destination location to head towards, and a NavMesh, so that it can plan a path, avoiding obstacles.

We created two possible waypoints to be the location for our NPC to move towards: Capsule-waypoint-0 and Capsule-waypoint-3.

The C# script class called WaypointManager has one job — to return a reference to the next waypoint that our NPC should head towards. There are two variables: wayPoint0 and wayPoint3 that reference to the two waypoint GameObjects in our scene. The NextWaypoint(…) method takes a single parameter named current, which is a reference to the current waypoint that the object was moving towards (or null). This method's task is to return a reference to the next waypoint that the NPC should travel towards. The logic for this method is simple—if current refers to waypoint0, then we'll return waypoint3, otherwise we'll return waypoint0. Note that if we pass this null method, then we'll get waypoint0 back (so, it is our default first waypoint).

The C# script class called ArrowNPCMovement has three variables: one is a reference to the destination GameObject named targetGO. The second is a reference to the NavMeshAgent component of the GameObject in which our instance of the class called ArrowNPCMovement is also a component. The third variable called WaypointManager is a reference to the sibling scripted component, an instance of our WaypointManager script class.

When the scene starts, via the Start()method, the NavMeshAgent and WaypointManager sibling components are found, and the HeadForDestination()method is called.

The HeadForDestination() method first sets the variable called targetGO to refer to the GameObject that is returned by a call to NextWaypoint(…) of the scripted component called WaypointManager (that is, targetGO is set to refer to either Capsule-waypoint-0 or Capsule-waypoint-3). Next, it instructs the NavMeshAgent to make its destination the position of the targetGO GameObject.

Each frame method called Update() is called. A test is made to see if the distance from the NPC arrow object is close to the destination waypoint. If the distance is smaller than twice the stopping distance, set in our NavMeshAgent, then a call is made to WaypointManager.NextWaypoint(…) to update our target destination to be the next waypoint in the sequence.

There's more...

There are some details that you don't want to miss.

More efficient to avoid using NavMeshes for waypoints

NavMeshes are far superior to waypoints, since a location in a general area (not a specific point) can be used, and the path finding the algorithm will automatically find the shortest route. For a succinct recipe (such as the above), we can simplify the implementation of waypoints using NavMeshes for calculating movements for us. However, for optimized, real-world games the most common way to move from one waypoint to the next is via linear interpolation, or by implementing Craig Reynold's Seek algorithm (for details follow the link listed in the Conclusion section, at the end of this chapter).

Working with arrays of waypoints

Having a separate C# script class called WaypointManager to simply swap between Capsule-waypoint-0 and Capsule-waypoint-3 may have seemed to be a heavy duty and over-engineering task, but this was actually a very good move. An object of the script class called WaypointManager has the job of returning the next waypoint. It is now very straightforward to add a more sophisticated approach of having an array of waypoints, without us having to change any code in the script class called ArrowNPCMovement. We can choose a random waypoint to be the next destination (see the Choosing destinations – find nearest (or a random) spawnpoint recipe). Or, we can have an array of waypoints, and choose the next one in the sequence.

To improve our game to work with an array of waypoints in the sequence to be followed, we need to do the following:

  1. Copy Capsule-waypoint-0, name the copy as Capsule-waypoint-1, and position this copy at (0, 0, 8).
  2. Make four more copies (named Capsule-waypoint-1, 2, 4, 5), and position them as follows:
    • Capsule-waypoint-1: Position = (-2, 0, 8)
    • Capsule-waypoint-2: Position = (8, 0, 8)
    • Capsule-waypoint-4: Position = (-2, 0, -8)
    • Capsule-waypoint-5: Position = (-12, 0, -8)
  3. Replace the C# script class called WaypointManager with the following code:
    using UnityEngine;
    using System.Collections;
    using System;
    
    public class WaypointManager : MonoBehaviour {
      public GameObject[] waypoints;
    
      public GameObject NextWaypoint (GameObject current)
      {
        if( waypoints.Length < 1)
          Debug.LogError ("WaypointManager:: ERROR - no waypoints have been added to array!");
    
        int currentIndex = Array.IndexOf(waypoints, current);
        int nextIndex = ((currentIndex + 1) % waypoints.Length);
        return waypoints[nextIndex];
      }
    }
  4. Ensure that Sphere-arrow is selected. In the Inspector panel for the WaypointManager scripted component set the size of the Waypoints array to 6. Now, drag in all the six capsule waypoint objects called as Capsule-waypoint-0/1/2/3/4/5.
  5. Run the game. Now, the Sphere-arrow GameObject will first move towards the waypoint 0 (top left, and then follow the sequence around the terrain).
  6. Finally, you can make it look as if the Sphere is following a yellow brick road. Import the provided yellow brick texture, add this to your terrain, and paint the texture an oval-shaped path between the waypoints. You may also uncheck the Mesh Rendered component for each waypoint capsule, so that the user does not see any of the way points, but just the arrow object following the yellow brick path

In the NextWaypoint(…) method, first we check in case the array is empty, in which case an error is logged. Next, the array index for the current waypoint GameObject is found (if present in the array). Finally, the array index for the next waypoint is calculated using a modulus operator to support a cyclic sequence, returning to the beginning of the array after the last element has been visited.

Increased flexibility with a WayPoint class

Rather than forcing a GameObject to follow a single rigid sequence of locations, we can make things more flexible by defining a WayPoint class, whereby each waypoint GameObject has an array of possible destinations, and each of these has its own array and so on. In this way a di-graph (directed graph) can be implemented, of which a linear sequence is just one possible instance.

To improve our game to work with a di-graph of waypoints, do the following:

  1. Remove the scripted WayPointManager component from the Sphere-arrow GameObject.
  2. Replace the C# script class called ArrowNPCMovement with the following code:
    using UnityEngine;
    using System.Collections;
    
    public class ArrowNPCMovement : MonoBehaviour {
      public Waypoint waypoint;
      private bool firstWayPoint = true;
      private NavMeshAgent navMeshAgent;
    
      void Start (){
        navMeshAgent = GetComponent<NavMeshAgent>();
        HeadForNextWayPoint();
      }
    
      void Update () {
        float closeToDestinaton = navMeshAgent.stoppingDistance * 2;
        if (navMeshAgent.remainingDistance < closeToDestinaton){
          HeadForNextWayPoint ();
        }
      }
    
      private void HeadForNextWayPoint (){
        if(firstWayPoint)
          firstWayPoint = false;
        else
          waypoint = waypoint.GetNextWaypoint();
    
        Vector3 target = waypoint.transform.position;
        navMeshAgent.SetDestination (target);
      }
    }
  3. Create a new C# script class called WayPoint with the following code:
    using UnityEngine;
    using System.Collections;
    
    public class Waypoint: MonoBehaviour {
      public Waypoint[] waypoints;
    
      public Waypoint GetNextWaypoint () {
        return waypoints[ Random.Range(0, waypoints.Length) ];
      }
    }
  4. Select all the six GameObjects called Capsule-waypoint -0/1/2/3/4/5, and add to them a scripted instance of C# class called WayPoint.
  5. Select the Sphere-arrow GameObject and add to it a scripted instance of C# class called WayPoint.
  6. Ensure that the Sphere-arrow GameObject is selected: in the Inspector panel for the ArrowNPCMovement scripted component drag Capsule-waypoint-0 into the Waypoint public variable slot.
  7. Now, we need to link Capsule-waypoint-0 to Capsule-waypoint-1, Capsule-waypoint-1 to Capsule-waypoint -2, and so on. Select Capsule-waypoint-0, set its Waypoints array size to 1, and drag in Capsule-waypoint-1. Next, select Capsule-waypoint-1, set its Waypoints array size to 1, and drag in Capsule-waypoint-2. Do the following until you finally link Capsule-waypoint-5 back to Capsule-waypoint-0.

You now have a much more flexible game architecture, allowing GameObjects to randomly select one of several different paths at each waypoint reached. In this final recipe variation, we have implemented a waypoint sequence, since each waypoint has an array of just one linked waypoint. However, if you change the array size to 2 or more, you will then be creating a graph of linked waypoints, adding random variations in the sequence of waypoints that a computer controlled character follows for any given run of your game.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.30.210