Objective: code the enemy AI for patrolling behaviour
Cool stuff ahead!
Our goal today is to make the guards patrol on pre-chosen paths. In order to do so we have to acknowledge the fact that guards and Darren share the same features (in this case the navmesh agent) and the only difference is that Darren is guided by human hand. So, the first thing to do is to give guards a navmesh agent component.
The best way is to select all of them and add the component at once. Also adjusting the agent size can be done at once.
Done? Good! Let’s design a little. In the snapshot you can see the paths I chose for the guards: an L-shaped path around a display case, a two ways shape, and the last guard would be still and idle in one position (lazy dog!).
Since guards have navmesh agents, their movement will be achieved through the use of
destination property or
.SetDestination() method. In both cases a Vector3 holding the target world position is needed.
The logic I want to code is going to work like this:
- One script to rule them all! So abstraction is needed. We are going to feed each guard with a “path” and apply always the same logic
- Paths will be represented by waypoints: ordered Transform objects holding world positions
- Guards will travel to the next waypoint in list after reaching the current one. When the last is reached, the path will be walked in reverse
Let’s create the waypoints for the guard patrolling the L-shape route. The trick I used here was to duplicate the guard, move the duplicate at the chosen position, strip it of all children and component until just the transform is left. Then choose a proper name.
Before cleaning the guard duplicate object, remember to unpack its state of prefab!
For this particular guard we will have:
- PointA: the starting position under his very feet.
- PointB: at the end of the display case, the elbow of the L.
- PointC: next to the other display case, the end of the L.
The idea is to use something like
waypoint.transform.position as destination. So let’s code! The script will be EnemyAI.
The script will have some fields: a handle on navmesh agent, the personal list of waypoints (transform), an int saying which is the current target (waypoint to be reached) and an int saying if the guard is on the original path (1) or its reverse (-1).
.Start() method, we safe-check the list, set the first target as the 0th and set the corresponding position as destination for the agent. With this code nothing will really happen, since PointA is the guard starting position, but let’s move on.
Then we have this
NextTarget() method. Here we calculate the guard’s next step:
directionhas only +/-1 value, so we check if adding that value to the current target id result in a correct new id or out of bound (greater than list count or smaller than zero). In case we fall out of bound, we know that end or start of path has been reached, so we have to invert the direction.
- With this in mind we actually calculate the next step by adding 1(-1) to the current id.
Notice that no check on the list are performed, this is because the entire method would be called only upon positive checks, as you will see.
.Update() method, we safe-check the list, then we check if the current target position has been reached and in that case we give instruction to move to the next one.
The current target position has been reached if the guard position equals the waypoint one, keeping in mind that using the == operator will take into account a possible error of 1e-5.
This may be too restrictive sometimes. Indeed, you may notice an odd behaviour: when you play the game, the guards’ y-position changes!! (while the waypoints, you set on the basis of their old position, don’t change). This is because the navmesh agent actually grounds the object to the floor. The difference between the old and the correctly grounded position is most probably greater than 1e-5. So you have two possibilities:
- Start the game, copy the y-value of the grounded guard, exit play mode and apply this value to guard and waypoints as well. (Tested)
- Rewrite the logic to check target reaching
Where you basically check if the guard is within a range of value
_tol (of your choice) from the waypoint.
Don’t forget to attach the script to all the guards and populate lists and variables.