Implementing AI Movement in Unity 2021

Our project has very simple requirements for the AI characters.
We simply want them to run out a door, up some ramps, and into another door.

Creating the Starting and Finish Lines
I’ve already placed an NPC in the starting room of the scene and lined it up with the doorway.

I’ll use that as my starting point, which may or may not need to be adjusted in the future.

Before I can make use of the Robot game object’s position, I’ll need to create some empty game objects to store this information.

I’ve created a parent empty game object for general tidiness and two child empty game objects for the start and end positions.

I’d like to note that this is a handy trick to ensure that the when we spawn our game objects (Robots), they will be positioned correctly since we have used a stand-in Robot game object to set our position.

Or so I thought.

The gizmo for the Start waypoint is showing at the feet of the robot game object, so we may need to adjust this once we test it out.

Note that I also tried copying only the Position (World) from the Transform component of the Robot 1 game object to the start waypoint with the position remaining the same.

Anyways, lets move on as we can fix this later.

I’ve also positioned my end waypoint at the other side of the scene beyond the 2nd doorway.

This will be out of the player’s sight lines so we will be able to remove the NPCs here without notice.

We’ll also be using a pooling system to conserve system resources, but we’ll get to that when the time comes.

We need to add the Nav Mesh Agent component to our Robot game object or it won’t be able to make use of the NavMesh system.

We want to keep an eye on the “Base Offset” setting if our Robot seems to either be floating along or partially buried in the floor of our scene.

The green lines indicate the base offset position which looks good for now.
Again, we’ll adjust if needed.

Since I want the door to open when the Robot approaches it, I’ll tag my Robot.

Later in code, we’ll use CompareTag to implement this behavior.

I’ve created the RobotAI script to handle the movement logic for our Robot.

Then I assigned it to the Robot game object.

Now, I like to keep the original prefabs I use separated from the modified versions of those prefab game objects.

So, as shown above, I’ve removed the link to the original prefab and created a new prefab for the Robot_1 game object.

Coding the RobotAI Script
We’ll need to add the “using UnityEngine.AI;” statement to our script and several class variables as shown above.

Since I will be coding a finite state machine for the Robot AI that includes running, hiding, and dead, I’ll need more than just the start and end waypoint positions.

Hence the array _waypoints;

By default the robot will run straight to the end waypoint.
However, if a bullet collider either hits the robot or passes near the robot, I want the robot to hide at the nearest cover.

I’ll assign the cover as waypoints in the future.
For now, I just want to get the waypoint system working in code and the robot running from start to finish.

In the Start() method, I want to initialize these variables.

We assign the _navMeshAgent from our Robot game object’s components, get and assign the _waypointsParentGO variable and then use it to iterate through all of the child transforms which are waypoints along the path where cover can be found and assign those to our array.

Once we have all of those waypoints assigned, we take the last transform in the waypoints array and assign it as the _waypointEndPosition variable.

We then default the _currDestination variable to the _waypointEndPosition so that the robot will run towards the last waypoint by default.

Later, we’ll add code that changes _currDestination so that the Robot will run to cover as needed before resetting _currDestination to the end position.

Before we continue on, I want to update the class variables section and Start() method.

The more I thought through the cover mechanics and coded out the methods for it, the more my original code changed.

Not too much though!

As you can see, I’ve added an enum called “CoverStatus” and create a variable based on it.
This will let us easily monitor the AI state.

The Start() method didn’t change too much, just needed to default the _coverStatus variable to None.

Moving on to some new code, we have our Update() method.

We’ll be calling two methods here, DetectHitorNearMiss() and Move().

The DetectHitOrNearMiss() method will be fleshed out in the future once I add some colliders.
One collider will detect hits, and another will detect near misses.

For now, this method is almost pseudo code.

The Move() method checks if the NavMeshAgent should be stopped in case we somehow missed this in our cover code and stops the Agent if appropriate.

Otherwise, it checks if the cover status is running towards cover, and if the remaining distance means the Agent has arrived.

If so, it stops the agent, changes the cover status, and calls a coroutine to pause the movement.

With the cover logic during the move phase covered, we can now check that the cover status is none and that we are currently moving towards the end position waypoint.

We still have more work to do here, but we’ll come back to that later.
For now, let’s cover the remaining little bit of code I already have in place.

Here we have the coroutine WaitToRun() that will keep the AI behind cover for 3 seconds before continuing its run.

I also have some methods that may be used in the future, but are definitely subject to change.

I know that when the Robot reaches the end waypoint, a spawn manager will reuse it by just repositiong at the start waypoint.

I’ll need to reset some values when that happens.
ResetValues() is currently public, but it probably will end up being private.

Now, when running the game, we find that nothing happens!

I’ve left out one crucial line of code in my Start() method as shown above.

Alright, now if we run the game, we’ll see our Robot *moving* across the NavMesh.

In the next article, I’ll cover animating the Robot so that it looks as expected when running.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store