-
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.