So we have previously set
the player up so we can see that the model
has a gun built in, so let's go ahead
and get the player to
a point where the player can defend themselves.
What we're going to do is give the enemy the ability to
have health and then we're going to give the player
the ability to take that health away.
So we're going to go through the enemy health script next.
So we'll start off by finding it in
Scripts - Enemy folder.
Then we can drag and drop that
on to the Zombunny in the hierarchy.
So that's going to apply the script for us.
On the Zombunny, so we can see that there's
a Death clip
which we're going to apply.
Use the circle select button
and that's opened up the context sensitive menu so we can
find Zombunny Death. Apply that.
So now everything's setup for us, we can
edit that script or view it
and see what's going on in that.
Double click the icon to open it.
As always we've got our public variables at the top.
And very similar to the player's health we've got
startingHealth and a currentHealth.
They work exactly the same as the player.
The next one is the sinkSpeed.
When these enemies die
it looks a bit funky to just have them
lay there and then suddenly disappear.
So what we're going to do is make them sink through
the floor, so as soon as they've
finished flopping over and dying they sink
through the floor, and that's how fast
we want them to sink through the floor.
Later on in the day we're going to start doing
scoring for this game and
so each enemy needs to have a scoreValue,
how much they increase our score by
and that is the scoreValue.
And we've got the deathClip
that they play when they die.
We've got some private variables
starting off with the animator component reference.
Then we've got a reference to the
audio source, we've also got a reference
to the hit particles.
If you remember we dragged that on as a prefab
and applied it as a child object.
Likewise we've got a capsule collider reference.
Then we've got a pair of boolean variables.
We've got IsDead, which works exactly
the same as with the player.
And IsSinking, so they don't immediately start sinking
because we want to see the animation so we need to have
separate bools to determine
whether or not they're sinking and whether they're dead.
Next we've got our awake function.
Which is going to setup our references as usual.
The first two, as we've seen before, GetComponent,
the type of the component that we're going to find
animator, audio source,
but with hitParticles we need to
find a component in the child object hitParticles.
So what GetComponentInChildren will do
will go through all of the children
game object and then find
the first particle system and return that.
Again, we've got GetComponent to find the capsule collider.
Then at the end of the awake function
we're setting the current health to the starting health.
In update
all we're doing is we're checking whether or not
the enemy is supposed to be sinking or not.
If it is sinking then we're going to translate
the transform, and that means just move it.
So we're going to move it in a negative-up
direction, down.
and we're going to do that by the sinkSpeed
per second, so that's that Time.DeltaTime thing.
If we do that then we're moving per second
instead of per frame.
So just a quick note about translate.
Previously we used move position to move
this thing but we're going to be no longer using
physics when the enemies die so
we can go ahead and use translate without
worrying about losing sync with physics.
Next we've got, again very similar
function to the player's health we've got
a public function, so again that means it
can be called from another function
and that's where we'll be calling it from.
Another script.
Another script, sorry.
But this time we've got the integer
of how much damage is going to be taken
but also the hitPoint,
so where has it been it?
And we'll be using that hitPoint to
move the particle system around the enemy
so that fluff is flying out wherever it gets hit.
Right, so the first thing we want to do in this function
is check if the enemy is dead.
If it is dead then we don't need to do
anything so we're going to return out of this function.
Assuming we are not dead, or the enemy is not dead
we can continue on with this function.
Then since we've taken damage we want to play
the Hurt sound effect.
We'll then lose the amount of health
from our current health.
Next what we're going to do is
find the hitParticles, so that's the particle system
Find the transform that that is on
and move that position to the hitPoint.
So we've got a child game object
finding that position and moving it to
where ever we've been hit.
So note that we haven't actually defined the hitPoint
but it's going to be passed in to this
TakeDamage function
using that second argument, this vector3.
So we're going to send it wherever we call TakeDamage
and you'll see that a little later on.
Okay, so after we've moved
the position of the particle system
we can then play the particle system, so the fluff starts flying out.
Last in this function, we're going to check
if our current health is less than
or equal to 0.
If we've run out of health then we'll use the Death function.
So the Death function.
First of all we set isDead equals to true.
Then we set the capsule collider to a trigger.
So what that means is because you don't
actually physically hit triggers
if the player is running along mowing down enemies
then when they die they won't become
an obstacle any more, it can keep on moving
and keep on mowing through them.
We set the animator trigger Dead
so the enemy knows it's dead
it performs it's Dead animation.
And lastly we're going to set the audio
source to play the Death clip.
So we change the clip that it's got to play to Death
and then make it play.
Okay, so we've got a public function here
called StartSinking and we'll discuss why it's
public in a minute but for now we'll just
go through what it does.
In this we're going to find references
to the nav mesh agent
and disable it.
And then we're going to find a reference to the
rigidbody component and set it to isKinematic
The reason we're going to set it to kinematic is
that when you move a collider
in the scene Unity will try and
recalculate all the static geometry
because it thinks 'okay, the level's changed,
I need to rethink about this.
But if you've got a kinematic rigidbody
and you're translating this object
then it will ignore it, so that's why we're doing that.
Real quick here, we see GetComponent
.enabled = false;
so if we were trying to turn
off a game object
we say .setActive
and in parenthesis say false.
Nav mesh agent's a component so we say
.enabled, so just keep that in mind.
If you see .setActive = false
that's a game object and we're turning the whole
game object off.
Here we're doing .enabled = false
That means I'm not turning off the whole game object
just this one component of that game object.
Since we're starting to sink isSinking is true.
And lastly we're going to destroy the game object
after 2 seconds, so basically
it's started sinking, it's going through the floor,
after 2 seconds we're not going to see it any more
we can get rid of it, so we'll destroy the game object.
And that's the end of that function
and the end of this script as well.
When we're done with that we can hop back over
in to Unity here.
The enemy now has health and so one of the
things that we want to do is we
want to make the enemy's
ability to attack dependent on whether or not
the enemy is alive.
Sorry to interrupt you Mike, I've just remembered that we've
missed something out.
So we had that StartSinking function
and it was public
but we never called it.
The reason we never called it is because
it's on an animation event.
So all these enemies, they have an animation event
where they flop and then die.
And what we can do in Unity is say
somewhere along the line of that animation
we're going to say 'at this point
try and look for this function
on the game object, somewhere on the game object
there will be a function called StartSinking.
So it'll look for that and then if
it finds it it'll play that function.
This was setup already.
Yes, this isn't something that you have to do,
this is something that we've setup for you.
So this is something that is already there,
and basically what this says is at this
mark, which is something that is already in the animation
it's not anything that you guys have to do,
it's going to attempt to call a method called
StartSinking.
Now up until this point there has been
no such method, alright.
But now we've created one and we've added
it so now it knows
I have a method called StartSinking and
that will happen when it's time comes.
Normally if the animation were to play
and that function were to not be there you would
get an error saying 'hey, I'm trying to call this function
and one does not exist.
We did not get an error because we have yet to
have any way of killing the enemy.
If we had we would have seen an error but
if we don't then we do not see an error.
The function was public and so this animation will
automatically call that function when the time comes.
Animation events are really useful,
you can use them for all kinds of things.
A real common use would be footsteps
if you had a single function to call a
footstep at a particular point in an animation
because obviously you can pick a particular frame
where you want this to happen.
We've got this fairly roughly placed
and it just means that as soon as he just leaps up
we'll start sending him down as well
as his falling animation,
which actually means that we're not getting the bounce on
on the floor, but we could move it along if we wanted
to but we're not going to bother doing that,
you can play around with that later on.
Now that we have the Enemy Health script
it's important for us to pair the Enemy Attack script
to that so that the enemy does not
attack once they're already dead.
What we'd like to do is edit
the Enemy Attack script and uncomment
those lines that we previously saw commented.
Now we could locate the script in the project view
and double click it and open it
but I just want to run through another way that we
could open that script.
What I'm going to do is click on the Zombunny,
and I am going to find the
Enemy Attack script, the Enemy Attack Script.
And of the script components the first property is always
the script itself.
Every script component has a first property called
Script and the value of that is itself.
So if I click on it what it does is highlight
for me in the project view that script.
So I'm like 'hey, which script is this?',
you click on it, there it is.
But I can then double click on it here
inside this property and it again will
open up inside Mono Develop.
So that's another way to open the script up.
So this is now a part that you're all going to want to
follow along with because we're now going to remove
this commenting while we explain what
the comments were for.
I'm going to come up to the top here
and the first bit of commenting I see
is a commented out variable
that references EnemyHealth.
Obviously we had to comment that out before
because EnemyHealth didn't exist on the enemy yet
so we had to keep that commented out.
Now we're going to uncomment it so we can now have a
reference to the enemy health script.
Next we're going to go to our awake function,
and again, since we have this variable,
we now have to get that reference and we dothat
by saying enemyHealth = GetComponent,
and the component name is the name of the script,
which is EnemyHealth.
And then we're going to come down here towards the bottom
and we're going to notice this.
And this is not the commenting you've seen before.
There we go, it's about as close as it'll let me get.
So this is called a Block Comment
and what block comments enable me to do is
comment many, many, many lines
or pieces inside a line
or whatever, right? So what we have here
is what's called a Block Comment.
And we can see that here's
the start of the block comment,
and here is the end of the block comment.
So it's / all the way through to /.
And anything between there is commented out.
And you'll see that these parentheses
or parenthesis is not commented out
because only the things between the block
comments get commented out.
So what we want to do is remove the block comment.
So when we start to remove this
we'll see everything gets commented out and then
nothing's commented out.
So what this line says now
if you recall previously it says
'hey, if it's time to attack
and the player is in range
we attack the player'.
Well there's one more step, one more decision
you have to make, we have to say
'if it's time to attack
and the player is in range
and we're not dead'
and at that point we attack.
So at this point now with all of that stuff
uncommented now the enemy will attack the player
correctly like they had been and they'll
also stop when they're dead,
which is obviously an important part of this whole dynamic.
Be sure to get that stuff done. When you're finished
be sure to save,
save the script and return,
which will bring us back in to Unity.
The enemy is cool, we can leave it alone for now.
Now let's go ahead and get this
player ready to rock and roll.
There's a few things that we're going to need to do.
The player already has this mesh and the mesh
has a gun, so the player is already setup
with his weapon of choice so what we want to
do is we want to make it behave appropriately.
So there's some stuff we want to add to it.
The first is we're going to add a particle component
which is going to enable the gun to
spit fire, which makes it kinda fun.
We're also going to add the ability for
it to have sound so we can hear
when the player is firing.
We're going to add a light so that the player
illuminates the scene when firing
and then we're going to add a line renderer
and the line renderer is actually going to be that
line that we're firing out
to make it look like we're firing bullets
or lasers or whatever,
and all of these things are going to make this
a much more fun look and feel to the game.
So what we're going to do is go to the prefabs folder
and we're going to locate gunParticles.
Unlike the hitParticles of the enemy
we are not going to click and drag this prefab
anywhere near the player.
Instead we are going to copy
a component off of it.
So we're not going to use this prefab
we're just going to use a piece of this prefab.
So if we look over here in the inspector
we see the particle system that is
a part of that gunParticles
and I can click the cog, or gear
right here and I can select Copy Component.
I'm not actually interested in the game object or the
prefab I'm interested in the component that's on it.
Once I've copied that component
I'm going to go to the player in my hierarchy
and I'm going to expand it.
And I'm going to look for GunBarrelEnd.
Since the player has a collider
and the player has all these pieces and parts
I'm actually really interested in the
tip of the gun so that I can
align bullets and everything with
the tip of the gun and get
shooting to happen the way we want it to.
With GunBarrelEnd not Gun,
not Player, GunBarrelEnd selected
in the hierarchy I need to
locate a cog, there's always a cog
in the upper right hand corner of the transform component.
And I'm going to click that and I'm going to select
Component As New.
And there's my particles.
So at this point the particles are
on the end of the gun.
Now they're not set to play.
We going to use scripts to say when
those particles should start firing off.
So now we have the particle system on the gun.
Now let's minimise the particle system by
clicking the little arrow next to it
because the particle system itself takes up so much space
so we're just going to collapse that down
so that we have some room to work.
So we have our particle system and the next thing
we want is a Line Renderer.
The line renderer is going to be the visual
component of shooting and we're going to add that
by going to Add Component and I'm just going to type Line
and find Line Renderer.
I'm specifically typing in line because I can't
remember where it is otherwise.
Now a line renderer appears
as a component in our game object
and so what the line renderer is, like I say,
it renders a line, and so there's some settings
that we've got to do because as we can see right now
that doesn't really look like a bullet and that doesn't
really look like a gunshot, it just looks like
a giant magenta block.
So we need to set this up appropriately.
The first thing we're going to do is
expand the Materials drop down, and you can see here
I can just expand that by clicking the Materials drop down.
And I'm going to look for this Element 0.
And right now the reason this is
that hideous fuchsia colour is that it doesn't have
a material, it doesn't know what it's supposed to look like.
So what we want to do is give it
a colour so we're going to use the
circle selector, we're going to click on that,
and we already have a material created.
It's just a basic line renderer material
which we have cleverly named LineRendererMaterial.
So if you use the circle selector and look for
that list you should see a line renderer material.
Double click on that and it will apply it
to the gun.
What we want to do is and manage this line renderer better.
As we can see it's still, while it's still a
better colour, it's the colour of our laser
it's still way too big, so we definitely want to manage this.
I'll go ahead and collapse the material
back down as we're done there.
I'm going to expand Parameters and there's two things
I'm interested in, the first is Start Width
and the second is End Width.
And what we're going to do is set these up
to be the same value.
Because they're the same value the laser will
consider them to be the same size.
If they were different values it would either flare out in a cone
or narrow in or whatever.
And so for each of these we're going to specify
.05.
That's not 0.5, that's .05.
We're going to do that for both the start
and the end width.
Once that is done we are going
to go ahead and disable this component.
And the way that we're going to disable this component
is with this checkbox right here.
Right next to the name Line Renderer I'm
going to turn it off
And the reason I'm going to do that is the game doesn't
start with you shooting, the game starts with you
not shooting so I'm going to turn it off and only
turn it on once the time comes that
you have actually fired.
Now when we fire the gun what we want to do is
we want to have the gun flash and light things up
and in order to do that we need a
light so I'm going to go ahead and
collapse Line Renderer and now I'm
going to add a light component.
So I'm going to click the Add Component button
and I'm going to go to
Rendering and I'm going to select Light.
What that's going to do is add a light
to my gun, which we can see there.
What we want to do now is give this
the settings that will be appropriate
to this particular gun.
So the first thing to do is choose a colour
and we have this little eye dropper,
we can use the eye dropper just like you'd expect,
pick a colour out of something,
or we can just click next to it
and actually use the gradient/color picker
to pick a color.
I'm going to pick a yellowish color.
If you want to pick a different color go for it.
But I kind of want my light to be the same color
as my line renderer so I'm going with yellow there.
So now when we fire the weapon
there will in fact be a yellow light.
Now again like the line renderer we're not
starting firing so we're going to
disable the light.
So we'll just uncheck it there
and the light will be turned off until
we turn it on when we fire.
And so the last little bit we're going to
add is we need an audio source
so the gun can play the firing sound.
So what we're going to do is click Add Component.
We're going to choose Audio and Audio Source.
Now once the audio source is on the game object,
again just like we've done in the past we're going
to use the circle select picker
and we are going to locate Player GunShot.
Double click to add that and we are going to
uncheck Play On Wake.
We also want to be sure
that we do not have loop because
that will just drive everybody nuts.
So no play on awake, no looping,
That would be completely unnecessary.
At this point the gun is setup.
So now we can add the scripts
that's going to allow the player to attack the enemy.
So to recap, the enemy has health,
we've added the health script to the enemy which will
control when the enemy can attack and how the enemy dies.
The gun has particle effects, lights, line renderers and sound.
Now the last piece of this puzzle
is to give the player a script that's
going to allow the player to actually
fire the gun and harm the enemy.
So in the Scripts - Player folder
we're going to look for the PlayerShooting script.
And just like the previous scripts we're going to
click and drag this
and we are going to place it on the GunBarrelEnd,
not on the Player,
on the GunBarrelEnd.
So if we select the GunBarrelEnd we should see
the PlayerShooting script there.
So again ensure that it is not
on the Player, it is on the GunBarrelEnd.
And now let's go ahead and open up the PlayerShooting script.
Save your scene first. And let's look at what this script does.
The first thing we have is
these public variable declarations here,
the first is public in damagerPerShot
and we can see that every bullet is going to do
20 points of damage.
We also have a public float timeBetweenBullets
which again is going to control how quickly
our gun can fire, obviously we reduce that value
if we want to make our gun fire more quickly.
Then we have public float range
and that's how far bullets can go.
In this case they're going to be able to go 100 units
which is actually a really far distance.
Then we've got some private variables here
the first is a float timer,
and the float timer, just like the enemy attack,
it's going to keep everything in sync,
it'll make sure we can only attack when the time is right.
Then we have a Ray shootRay.
If you recall we have a gun,
we're firing so we're going to use this ray to actually
raycast out and figure out what it is we've hit
with these bullets, and that's how we're going to hit things.
We then have a RaycastHit variable called shootHit,
which is going to return back to us
whatever it is that we've hit.
We're then going to have an int shootableMask.
So we remember the floorMask which dictated that the
raycast from the camera could only click on the floor.
So what we're going to have is a shootable mask
to make sure that we can only hit shootable things,
we only want to shoot things that we can actually shoot.
Then we have a particle system referenced
to gunParticles, you'll recall that's the particle component
that we added in our previous step,
so that just gives us a reference to it.
Here's our reference to our line renderer called
gunLine, right, so again just so we can reference this in the script.
Our audio source reference called gunAudio.
Our light reference called gunLight.
And then our float effectsDisplayTime, which is
how long these effects are going to be
viewable before they disappear.
In our awake function we're going to setup all of our
references so we want to
set out shootableMask to the appropriate values
by saying LayerMask.GetMask ("Shootable") ;
What this is going to return back to us is
the number of our shootable layer.
You'll recall the level or the obstacles and everything
is on the shootable layer and the Zombunny we
created is also on the shootable layer.
So by setting up this mask we can shoot pretty
much anything that should be shootable.
Then we have the gun particles which we're going to
get access to by saying GetComponent
Then we're going to do the same for line, audio and light,
just gunLine = GetComponent
gunAudio = GetComponent
gunLight = GetComponent
which is just giving us a reference
so we can directly access those.
Now in the update function is where we
control whether or not it is time to shoot.
So very much like with the enemy attacking
here we have a function that's going to manage that.
So the first thing we're going to do is
accumulate your time and we do that by
saying time += Time.deltaTime.
So we'll basically increase in size
as time progresses.
Then we're going to say
if(Input.GetButton ("Fire1") and you might be thinking 'what is Fire1?'
Remember earlier we talked about the
Input.GetAxisHorizontal, Input.GetAxisVerticle,
these input axis that are built in to Unity for us
Fire1 is one of those.
Fire1 automatically maps to the
left control on your keyboard or
your mouse0 which is your left mouse button.
So that happens for you automatically.
You can override it here but if you don't do anything
that's built in to Unity, so
so you can always say Fire1 and that's how you talk
about the left mouse button.
So what we're saying is
if the player has clicked the left mouse button
or pressed the left control key
and
timer is greater than the delay between our shots.
So if it's time to shoot and the player wants to shoot
by clicking the button,
then we're going to call our function Shoot.
Shoot is a function we've written further down and
we'll talk about that in a second.
So if it's time and you're pressing the button we're going to shoot.
Then we're going to say if the timer
is greater than or equal to the
timeBetterBullets times the
effectDisplayTime what we'll do is disable the effects.
So what that means is if we've fired
and then enough time has progressed we're going
to turn the light and the line renderer and everything back off.
So we don't leave it on consistently.
So the DisableEffects function is public
which means again it can be A) referenced
by another script, B) referenced by another script on another game object
C) accessed by animation events and so on and so forth.
Public basically gives us a lot of access to it.
And the function basically says
the light and the line renderer,
just disable both of those.
Again not SetActive but Enabled = false
because they're components.
Now the Shoot function itself.
This is where we do the physics
of actually firing the bullet and it's
actually a fairly complex function here
so we'll step through it nice and easy.
The first thing we're going to do is reset the timer
back to 0 because we're firing now and we're going to
reset the amount of time we have to wait between firing.
The very next thing we're going to do is play the audio
and then we're turning the light on.
Now we don't need to do anything with the light
or the audio, we basically need to say
Then what we're going to say is if the
particles are still playing
stop them and then start them again.
What you don't want to happen is you don't
want to go to play the particles
have them already be playing
thus they don't replay and we get
a disconnect between the visuals of firing
and the actual raycasting and physics that's happening.
So we say if the particles are playing stop and start again.
Also if that were to happen the stop
and start would be so far your eye wouldn't even pick it up.
It would just feel like you're just firing really fast.
Then we're going to say 'let's turn on our line renderer',
which is the actual visual element of the bullet.
So we turn that on, so we say
gunLine.enabled = true.
Then here comes the tricky part about using lines,
lines have 2 points right?
One end and the other end.
Well the first end we know, it's the end of the gun,
it's the barrel of the gun,
so we access that by saying gunLine.SetPosition 0.
Computers start counting at 0
so when we say SetPosition 0 we mean
the first position of the line,
which is right at the barrel of our gun.
So we specify that as transform.position
since the script is on the barrel of the gun.
So that's the first point.
But what's the second point?
We don't know yet, that's the part we need to calculate.
And that's the part that requires a bit of physics
and a bit of raycasting.
So if you recall we create a variable called shootRay,
which is the raycast from our gun
and so the first thing we want to do is
setup this ray so that we can utilise it.
The ray is going to start
at the tip of the gun, so
shootRay.origin = transform.position;
That's where the ray is starting off.
A ray starts at one point and
goes in some direction, so we need to specify
a direction for our ray, and we say
shootRay.direction = transform.forward;
and this is something James has talked about previously,
we generally treat forward in the Z axis
as forward, positive on Z axis as forward.
So as the gun is pointing directly away from the player
that is forward.
So if I say transform.forward what I'm saying
is 'that way',
so that the bullet will travel
directly along the line of this gun barrel and this gun.
So we have our ray setup, at the tip of the gun
that way, right, forward, so now we need to
actually use physics to fire it.
And so let me tell you what's going to happen
and then we'll walk through the code.
The idea is that we are going to fire a ray
forward 100 units, because that's
how far we say we can fire it.
If it hits something, what ever it hits is going to be returned
back to us and we're going to say 'the other end of
the line is there', whatever it is we hit
that's the other end and we draw the line.
If we don't hit anything,
we still want to fire, but if we don't hit anything
what we're going to do is say
'where's the other end of this line?
It's just way out there'
and it's actually such a long line
there's literally no way to see
the end of it so you just think
'cool, he's just firing', you don't really notice.
So we need to say shoot a ray, if it hits something
that's what we hit, and if it doesn't
then just draw a really long line.
We do that by saying
if(Physics.Raycast, so this again is that raycast function,
and we're parsing in to it the ray that we specify,
we're saying 'that way'.
And then we're going to say 'give us information
about what it is that we've hit'
and that's why we have this Out keyword
and the shootHit variable.
If we hit something the variable shootHit will be
like 'this is what you hit'
cool, we need that information so it's important to have.
Then we specify the range, and the range
is 100f, it's something that we specified
up at the top, it's a variable.
Finally we parse in our shootable mask.
Meaning this ray can only hit
the things we want this ray to be able to hit,
shootable things.
We're not interested in anything else.
If that ray hits something
we're going to go in to this code.
if it hits something what we're going to do
is we are going to get from
it the enemy health script.
I'm going to talk about that here in a second.
So what we're going to say is
shootHit was going to have whatever
it is we hit, we know we hit something.
So we're going to say
shootHit.collider.GetComponent
So whatever it is I shoot I'm going to say
give me your enemy health script and I'm going to store it.
We need the enemy health script so we can
reduce the life of the enemy.
But what if we shot a wall?
Or the ground?
Or some Legos?
Those do not have an enemy health script,
those are not able to
have health taken away from them.
So if we attempt to access that enemy health script
right away it's not going to work
and we're going to get an error and the whole thing's
going to stop working.
So what we need to do next is
we need to say if the enemy health script
does not equal null,
this is specifically in here to
show you that this
function will work even if that component does not
It's just going to give you null, and null means nothing.
It means one does not exist.
So now we need to check it. We need today
'hey, by the way, if that
actually exists we're going to do
enemyHealth.TakeDamage.
We're going to parse in our damagePerShot
and exactly where we hit it,
that's a really nice thing about raycasting too,
instead of just having a generic effect
if we shoot on the shoulder that's where
we hit it, the shoulder, wherever,
we get to put the effects exactly
where we hit it, which is pretty neat.
If you remember in the enemy health script
TakeDamage, if I just jump back to that,
very briefly.
We can see that there is a vector3 hitPoint.
So you can see that TakeDamage here
needs to have a vector3, needs to have a position
in order to move the hitParticles to
where they need to be
so in PlayerShooting that's exactly what we
provide using that raycast.
Whether or not whatever we've shot has an enemy health script,
regardless of whatever it is we still hit something
and thus we say gunLine.SetPosition (1), meaning the second point
and we parse in that point that we hit.
So whether we're hitting an enemy or the wall or
some Legos or whatever,
we now have our line
the beginning of the gun to the object that we hit.
1 line.
When we were prototyping this game
we just had the raycast shoot by
100 units but then we realised that actually it makes more
sense if the gunLine stops at
that point, we could expand upon
the idea to have effects like ricochets
and things like that but we didn't want to get too bogged down.
So actually what this is saying is
if we hit something
then we end that line renderers second
point or it's end point at
the point at which the ray hit it,
so when we shoot a bit of the environment
it's going to stop at that point,
likewise with the enemies.
So all of that happens
if we hit something.
What if we don't hit something? That's our else.
We're going to say gunLine.SetPosition
1 again because it's the second point,
the second part of the line, and what we're
going to say is shootRay.origin, so where the ray starts,
+ shootRay.direction * range;
So what that's going to mean is it'll take
the point and then it's going to multiply (0, 0, 1)
because that's forward in the Z direction,
by 100, which is the range, and it's going to say
this point and then take it way down there
and then that point, and then it's going to draw a line.
So it's really simple, it'll say
one end, the other end, we're going to draw a line between it.
So a lot of physics stuff in there but basically
we're just attempting to point at something
and if we're successful at
pointing at something we're going to draw a line to it
and take damage from it, otherwise we're just going to draw the line.
So the else there effectively
is giving us the ability to shoot in
a direction if we don't hit something on the
on the shootable layer.
So if you remember that the level is basically a box,
if you're aiming around in the other direction
and you're effectively raycasting off
of the screen you still want to see the
gun fire so we're just giving it a range
of 100, which is outside of the camera
frustum, the view that you can see so we're
just spraying bullets around even though we don't hit anything.
What we'll do now that the gun barrel
script is on there is save our scene
if you haven't saved in a while.
Now with the player selected
in the hierarchy we're going to
look at the Apply button in the upper
right hand corner
You'll recall we clicked and dragged the player
and turned it in to a prefab but the
prefab that we have on file is now
slightly different than the prefab we have in our scene.
The one on file doesn't have all the
changes we just made
If we would like to update the one we have
on file we have to click the Apply button
in the upper right hand corner.
That will apply all of the changes we've made
to the copy we have on file,
thus saving it, if you will.
The way that you notice the difference between
a prefab and a prefab that
needs updating is that somethings will appear
in bold, so if you note the difference between
Player Movement's properties and
Player Health's which is added after
that we made it in to a prefab
you'll see that these are in bold and when I update
or apply the changes and look back down
some of them change so that the things
that are still bold are things that are in the scene.
So now we know that's up to date we can carry on
using it in other scenes and if
there are instances of this prefab elsewhere
then you would see those changes are
automatically applied.
Let's go ahead and everyone
turn your volume down because we're about to test this.
Cool, let's see how loud it is.
Oh yeah!
That's what I'm talking about,
that's an American gun right there.
Just to reiterate, do not worry about
this error that you will see appearing at the
bottom of the screen, it's deliberate,
this actual mistake is a genuine mistake
that we want to show you how to fix.
What you will notice is that at the
bottom of the screen you have an error.
So just to give you some insight on
catching errors in Unity.
Sometimes there'll be a compile error,
in that you've written something wrong in a script
and the bottom of the screen
will show you where and what in that script has happened.
And this is all contained within the console.
So if you go to Window - Console
you will see that there are a lot
of errors saying all about the same thing,
'set destination can only be called
on an active agent that has been
placed on a nav mesh'.
So remember that the word 'agent'
is referring to the enemy,
the enemy has that nav mesh agent component
and set destination is the function of code
that we used to tell it to seek out the player.
So we said nav.setDestination player.position.
What is actually happening there is that there is
a problem in our Enemy Movement script.
What we can do is double click on that
error in the console.
Watch that one more time, I double click that and it
takes me to what's causing that error.
And if you recall earlier we mentioned
that there are some bits of code that are
commented out, that are disabled currently in this.
When we first made the enemy chase after you
we didn't have a concept of
the enemy's health or the player's health.
So having those things in our script
wouldn't have made any sense.
So now we do have those things.
The player can be killed or the enemy can be killed
and if it is then
setting it's destination on the nav mesh
doesn't make any sense any more
so it's creating an error.
So now what we can do is we can
bring back those references to
the enemy's health and the player's health
and every time we do an update
we want to check if both the player and the enemy
are alive before we say
setDesination, because if they're not
then we're in trouble.
I'm going to use a shortcut here
I'll teach you which is command /
which is a way of commenting out and entire line.
That's control / for you PC folk.
Yes, for PC, and we can do all of this at once.
We've re-enabled this if statement and
else structure around this to just check
those different scripts, so it's checking the
enemy's health, it's checking the player's health
and if they're both more than 0 then it should
keep trying to seek out the player.
But if they're not then we switch off the nav mesh agent.
So the error that we were having in Unity
is saying that setDestination is effectively invalid
and we're solving that by checking
if the player is actually dead to do it.
And likewise if the enemy has enough health
to be moving around.
If not we switch off that component
by using .enabled = false.
So uncomment those lines in Enemy Movement,
save the script and then return to Unity.
Basically all of Enemy Movement should
now be enabled, you shouldn't have any comments
in it at all and that should get you to the
same point as us.
Okay, so the last part of this
is to look at the Player Health script.
What we're going to do is
reopen the Player Health script,
so if you double click on it
so go in to Scripts - Player
double click on the icon to open it.
And you'll see that
on the player's death
we've got some commented stuff here,
so we want to be able to put that back in
so that when the player dies
he can no longer shoot his gun.
But we haven't got our references to PlayerShooting yet.
Because it's turning red it's saying
'I don't know what PlayerShooting is and I
don't know what DisableEffects is'.
It can't find the reference to that so it's
turning red to alert us that there's a problem.
So on line 19
PlayerShooting, reference to the PlayerShooting script
which we've named the same.
So this can cause some confusion.
We've got PlayerMovement, PlayerMovement
remember this is the type, which is the name
of the script, and this is the name of the variable.
Names of variables can of course be anything
you want them to be but if I name PlayerMovement
Blah I will have to write Blah anywhere else.
But we're not going to do that.
We've named it something sensible, which means it's going to
sync up with the other instances and work.
So the last thing we need to uncomment is
the GetComponentInChildren
for PlayerShooting because remember
that the PlayerShooting script is on the
GunBarrelEnd, it's not on the Player
so we need to find that component in the children.
And now we've got our references and we've already
uncommented the codes about shooting when dead.
They're no longer red.
So it should all work.
Make sure that you save your script.
It's very important, and switch back to Unity.
You can then go ahead and play
and test your game.
Now we can kill them, there's no more error
about the nav mesh destination.
It's a very easy game, 1 enemy.
And we can also be killed by them.
So that is the end of phase 7.