The simplest way to create an NPC that makes decisions about what to do, based on the location of
the player is Line Of Sight.
Line Of Sight works by calculating the angle and distance from the NPC to the player
and using this data to decide on what action to take.
It's a very easy way to create a purely reactive agent whose behavior is determined by whether or not
they can see the player.
To implement this, the NPC is given a field of vision.
This is a cone extending from the location of the NPC in its forward-facing direction, for the length
of its visible range and spreading across an angle of vision. To determine if a player falls inside this
cone,
we calculate the vector from the NPC to the player.
We then determine if the 'distance to the player' is less than the 'visible range' and if the 'angle to the
player position' is less than that of the 'angle of vision'. If both conditions are true, the player is visible
to the NPC.
Follow along as I show you how to set up this type of Line Of Sight NPC. Download the AIRobotGuard.unitypackage
that comes with this lecture.
Create a new Unity 3D project and drag-and-drop that package file into your Assets.
When you do, you'll get these 2 items. You'll get a Scene called RobotGuard (which you can open up and
you'll see the robot guard standing in the scene) and you'll get a folder of its artwork.
The other items, you're going to add.
So once you've got that ready to go, we're going to add in a first-person controller, so that we can walk
around and test our proximity to the NPC.
Go into 'Assets > Import Package' and bring in the 'Characters' package. That's the one with the first-person,
third-person etc. things in it, so bring all of those items in and that will give you a
Standard Assets folder in which you'll find Characters
and then we want the first person 'Prefabs' and the FPSController.
So grab hold of that,
drag it into the scene and just lift it up so that it's above the ground.
Now once you've done that, it will have its own camera on it.
So the camera that's in the scene, you can delete (or just turn that off).
Now to test this is working, just press Play and you should be able to wander around the environment...
like that.
Ok.
Press the Esc key to get your mouse back and stop playing.
Ok.
So back in Assets, we're going to create an AIController script.
I've already got one here but you want to 'Create > C# Script' and call it AIController. Inside
that script,
you'll add this.
So this is the basic script to get our Line Of Sight working.
First of all, we need to know where the player is, so we're going to have a public transform at the top
to pass through the player.
Next we have a bunch of variables about the behavior for the NPC itself.
So we have the usual 'rotationSpeed' and 'speed' to turn and move the character and then we have our
Line Of Sight values.
So we've got a visible distance (so this is how far away from the character it can detect the player,
in this case it's 20),
the visible angle is set to 30 (so it can see you at a distance of 20, within an angle of 30 degrees to
either side of it.
So this angle is like a half of the complete field of view which would be 60).
Then we have a shoot distance and the shoot distance is how close the character has to be in order to
start shooting.
So if it's not close enough and it has detected you, it can run up to you until it's close enough to
take a shot.
Next we're going to store a state and this state will help us determine what animations to run a little
bit later on. So the NPC will have 3 states, it'll have IDLE, it'll have RUNNING and it'll have SHOOTING
and you'll see those come into play in the Update.
Now down in the Update is where all of the work is happening.
The first thing we do is calculate the direction from the NPC to the player. Ok, so remember, to do that
it's the NPC taken away from the player position and that will give you your direction vector.
Then we work out the angle between that direction vector and the forward-facing vector of the NPC and
we can do that with our Vector3.Angle method.
So that gives us an angle in degrees.
After that, we're going to do an if-statement to test if the length of that direction vector is inside
of our visible range and our angle is less than our visible angle, then we're good to go.
After that, I'm setting the direction vectors 'y' to 0.
So the direction vector is going to be in 3 dimensions.
If your character is higher or lower than the robot, then having a different y-value will make it tilt
and if you don't want it to tilt up or down to look at your character, you want it to stay grounded with
its y-axis always facing directly up,
then it's a good idea to set this change in the 'y' always back to 0 after you've done the calculation.
Trust me, it's just going to keep things all on the horizontal.
We then set the rotation of the NPC with a slerp (like we've done in the past) to slerp gradually towards
the direction vector using our our rotationSpeed.
So with this code, if the player is within the visible range of the NPC, it will always be turning towards
it.
Then we just have to decide whether we're going to push the NPC forward, towards the player.
So down here,
after that, we've got
'if(direction.magnitude > shootDist)'
so that means we're out of range for shooting but we're still within range to run after the character
(because this is inside this if-statement up here),
so if we can't shoot, then we're going to run towards the player,
otherwise we're going to shoot
and so we're setting this 'state' value.
If the player is outside of our range, then we just set the state to IDLE with this 'else' here,
so make sure that you've got this if-statement correct,
otherwise these will go wrong for you.
Then after that (and outside of this if-else statement,
We have an 'if(state == "RUNNING")' then push the character forward, on this line here.
So this is the only time that you want to Translate the character, when it's in the RUNNING state,
otherwise it's standing still (IDLE) or SHOOTING.
Right, so save that, go back to Unity
and we're going to add that code (drag-and-drop it onto your robot),
select the robot, find where the code is and where 'Player' is exposed,
drag-and-drop your FPSController into there, so the A.I. knows about it.
All right.
Let's test this out.
Press Play and if we look around and find the robot...
there he is.
Now his animations aren't going to be working at this point.
We just want to check, if we move away,
notice how he's following us.
This is him chasing us
and when he gets close enough, he'll stop and then he'll go into the shooting
and if we turn, he'll also keep turning towards us.
So there should be a point (if we can get outside of his range) that he will actually stop moving altogether
which is about there
and then if we stay outside the range and walk around behind him (outside of his visible angle) we should be
able to sneak up from the side and he won't detect us.
So he's not detecting us because he's not turning towards us but if we move around within range (let's come out here) there, he detected
us, turned towards us and started to move within shooting range of us.
Ok, so that is the basic logic to this Line Of Sight.
Now that the logic for the Line Of Sight is there, we can add in the animations.
Now the robot I've given you has animations on it and set up ready to go.
If you click on the robot, you'll find that it has an Animator in the Inspector.
If you double-click on the RobotGuard, it will open up your Animator window for you and you'll be able to see
what's on there.
It's very simple.
It's set up really similar to the stuff we've used before and basically it's the animation states,
'idle', 'run' and 'shoot'
and they're all linked by transitions and triggers on those transitions.
So I've set those up, there's an 'isRunning' an 'isShooting' and an 'isIdle'
and they all link between these animations states.
So there's nothing you have to do for these, as far as setting that up
but what we're going to do is trigger them in the code based on the state that the NPC is in. Just by
the way of interest, this particular robot model came with a Unity project many, many years ago and
I can't even remember which version it was.
Maybe one of you can remember. Maybe it was version 2 or something because these animations were legacy
animations, so I've updated those for the mecanim system.
So let's go back to the code, back to our scene on there...Assets and open up the code.
First thing we need to do (as always) is grab hold of his animator.
So we set that up at the top and then in the Start, we grab hold of it, so 'anim = this.GetComponent'
for the Animator, like that
and then we have to set the triggers along with each state.
So down in the Update, let's go to the first one for RUNNING.
Now in here, we can put 'anim.SetTrigger("isRunning");' - remember to spell your triggers and capitalize
exactly the same way as they're set up in the parameters in the Animator.
Now that will set the trigger to cause the 'isRunning' animation to work.
The only issue with doing it this way is, if the character is already running it's going to re-trigger
and re-trigger and re-trigger and you can
end up with weird things happening with multiple triggers going off.
So we can actually test...'if(state != "RUNNING")' then we will set the state to RUNNING and set
the trigger to 'isRunning'.
So this says if you're not running, then start running
and if you're already running, there's no need to start running again.
The same thing for all of these other ones, so we're going to do the same with shooting. So it's
'if(state != "SHOOTING")', 'state = "SHOOTING";
and 'anim.SetTrigger("isShooting");'
and once again, for idle.
So now this will set our animations based on the state and it'll ensure that they don't get set twice
in a row and then right at the bottom, outside,
if we're running them we'll still perform our forward pushing
Translation.
All right, so save that
and let's go back and try it out.
So, press Play...he's going to start shooting (which is because we're right next to him)
and now if we get out of range of shooting, he's going to run after us
and then start shooting (I'm going to make my gameplay window a bit bigger) and if I manage to run out of range, he's
going to lose interest and he'll go back into his idle state that
you can see there
and we should be able to...
sneak up and because we're not inside of that range,
he's taking no notice of us and I can turn around...get back into range and he'll start shooting and chasing
us and if we turn while we're in range, he will turn.
So that is very simple
A.I. mechanics called Line Of Sight, to get your NPC to chase after the player based on a range
of visibility.