In this phase we're going to create
this shell prefab, it looks like this
and has a number of different components,
it's got a collider, a rigidbody, it has a light component,
and the idea with this is that obviously we'll
fire it from the tank, but to demonstrate what it does,
it has an overlapped sphere
this is a kind of sphere of damage that it can
give to any of the tanks that i lands near.
So in this video what I'm doing is slowly moving it
closer to the tank and what you should notice is that
that health is getting reduced by more
the closer that that bullet happens to be.
Or at least it will do in a moment.
Off we go, moving it closer.
The closer it is the more damage that will be applied.
Obviously if you hit a tank dead-on then
that will be a huge amount of damage.
That's the shell that we're going to make and then in the next
phase after that we'll handle the actual shooting,
but first off we're going to handle the damage,
or the thing that's going to parse damage to
the TankHealth script that we did before.
What I would like everyone to do is look in
the Models folder, in this folder
you will find a model called Shell.
If you select it it will look like this in the inspector.
A little note, if you want to inspect something,
drag around it, you can just drag in that preview window
at the bottom of the inspector.
So what we'll do is drag that in to the hierarchy.
If we drag and drop that model from
the project to the hierarchy.
Move your cursor over the scene view,
press F to frame selected
and you'll notice it's at the origin, roughly,
so it's near to 0.
So all I'm going to do is just to drag mine
up slightly so it's somewhere I can
see it and work on it, something like that is fine.
Remember because we're creating a prefab that
we're going to fire with the tank this isn't something that's
going to be in the scene when the game starts
just like the tank, so we don't need to worry about
where it's positioned for now.
So because we are making
this something that's going to interact with other objects
it needs to have a collider.
We'll use primitive colliders because they're
nice and performant, and given the shape of this
we'll use a capsule collider.
So with that shell still selected
in the inspector we'll hit the Add Component button
and I'm just going to type Caps
for Capsule and then
click on it to confirm, or hit Return.
With that capsule collider,
because when you are firing things
like a projectile as we are.
We don't want things to be bouncing off one another
and we don't expect this tank shell to
ever hit something and push it back,
we're expecting it to hit something,
create a call back in the script and then create
an explosion force.
So we don't want any physical interactions.
And when we don't want those we use what's called triggers.
If you hear someone refer to a trigger
or a trigger zone,
or a trigger area they're usually referring to
a collider that's been checked
as a trigger.
So if you check the Is Trigger checkbox on that,
what that basically means is that it doesn't have
it doesn't have a solid surface to it
it can intersect, but if can send a
call back to the script saying
'I've now entered another trigger' or
'I've entered another collider'.
So often trigger colliders are used for things like
doors in games, like when you put a big
cubic collider in front of
a doorway and then when the character enters that way
you automatically open the door.
It's a pretty classic use of that kind of collider.
Once you've added that component, the next thing
we want to do is actually set it up.
So we need to change the centre,
so the centre value for that is (0, 0, 0.2).
And the radius is 0.15.
And the height is 0.55.
Crucially you'll notice that it's not
looking in the right direction, so we set the direction
to Z axis, and then it should
pretty much match our shell perfectly.
Or to an amount of precision that would never cause issues.
It should look like that.
And then finally because this is a physics object
we need it to be under control of
the physics engine, and so what are we going to add?
A rigidbody, there we go, nice and loud.
So add our rigidbody component.
And it's good to go.
So these shells are going to fire
a projectile, they're not going to just keep going straight forward,
they're going to fall with gravity over time.
So just a quick recap of those two points.
Okay, so we've found the model named Shell in the Models folder.
We dragged it in to the hierarchy.
We've added a capsule collider component to the shell
and we've made sure it's a trigger so that that
trigger won't bounce off of anything
it's just going to pass straight through stuff
and then immediately be destroyed by the script
that we're going to write.
We've checked Is Trigger to make sure that is the case.
Then we set the direction to Z axis
and we changed the centre, so the centre is
basically where that originates from so you can
kind of offset your colliders if you need to, art-wise.
And the radius we've set to 0.15, the radius of the capsule.
And the height to 0.55
And we added a rigidbody to make sure that it's
under the control of the physics engine.
So we've got the shell
and currently the shell can fall down
and nothing is going to really happen.
It's just going to go straight through the ground
because it's not going to stop, it hasn't got a
physical presence, like I said.
So we don't want that, we want it to actually
explode and do stuff, so we've created
a shell explosion for you.
So if you look in the Prefabs folder
you will find a prefab called ShellExplosion.
So what we're going to do with that is make it
a child of the shell.
So if you drag from the Prefabs folder and drop
it on to the shell
It should look like that.
And when we simulate it
it looks like this.
A cloud of dust that kind of falls over time.
Okay and then the only other thing that we need
to do with that is to
add an audio source,
so similar to the tank exploding,
it needs to have visual effects and an audio effect.
So I'm going to click the Add Component button
and I'm going to choose Audio Source.
And then just like before we need an
audio clip, so I'm going to use the
circle select next to audio clip
and choose ShellExplosion to assign it.
Now we don't want this to play as soon as the shell
gets instantiated, as soon as the explosion appears,
so we're going to uncheck Play On Awake.
A quick recap, so we've got our ShellExplosion prefab,
we had to make it a child of the Shell game object
so it's going to travel with it and then do it's
thing as soon as it hits something.
We added an audio source to that
so that it's able to playback sound and we've used
circle select to set the audio clip to ShellExplosion.
We don't want it to play as soon as it enters
the world so we only want it to play
as soon as it impacts, so we uncheck Play On Awake.
Okay, the final thing we're going to do with the shell
so reselect the shell, the parent object,
is to add a light.
So as this travels across the
game board we want this to
light up the environment
and it helps pick out the shell, because obviously
you've got a gold shell, you've got orange sand,
it's kind of hard to see it and the light helps pick it out
so instead of creating a point light game object
we can just add that same light.
So if you've new to Unity, what you'll notice
if you go to Game Object - Create
is that you've got all these readymade
prefabbed game objects with several components on them.
So for example if you choose,
if there was a cube, then you've got
a bunch of different components on it.
But you could also create a light
which has just the light component on it.
We don't need to do that, we're just going to put the
component directly on to the shell,
so I got to Add Component - Light
So now we have to actually handle
how that behaves, how it's going to
impact and what it's going to do when it hits
a tank or when it hits the environment and the
kind of forces it's going to apply to other tanks in the game.
So what we're going to do is to look
in the Scripts folder and
find the Shell folder.
In that you will find a script called ShellExplosion.
So we're going to drag and drop ShellExplosion on to the Shell
game object in the hierarchy.
And then you should see
that along with everything else, the rigidbody,
the capsule collider, the light that we just added,
you've now got Shell Explosion (Script)
and we're going to double click on that to open it up for editing.
Okay, so in this one we don't have any private
variables but we do have a bunch of public ones.
So the first one is a layer mask.
Now you might remember at the beginning of the day
we put all the tanks on the Players layer.
So that means that when we cause an
explosion from the shell we can just
check for everything that's on the Players layer
and see which tanks we've hit rather than trying to affect everything.
So that will be available in the inspector
and we can choose which layer we want to affect.
The next is the explosion particle,
remember we just dragged on that as a child game object
we're going to get a reference to that so we can
play the particles when the shell explodes.
Likewise there's an audio source reference,
so that's for getting a reference to that audio source
so we can play a sound.
Then we've got a bunch of floats,
so the first one is the most
damage that it can possibly do,
the maximum damage, and we're setting that to 100.
so if you get a perfect direct hit
then you'll kill the tank outright.
But you can't get a perfect hit because
it's never going to be exactly on the position
of the tank because the collider is covering it.
So you're going to get close but no cigar.
The next one is the amount of force
that the explosion is going to do right at it's centre.
Likewise as the explosion radius
gets further out the tank will be
less affected by the explosion, you'll feel less force.
We've got the max lifetime, so we don't want
shells to exist forever
so if they happen to miss everything
we don't want them to stay in the scene forever so
we give them a lifetime,
and at the start of the scene we'll say
if it's not dead after 2 seconds just kill it anyway.
So it's pretty unlikely you'll ever see that,
it would either hit the ground or
some other object before that ever happened,
but it's just a failsafe that we'll write in.
So the last public variable is
the explosion radius.
And that is literally how far
from the shell it's going to affect when it explodes.
Okay, so let's look at these functions
and as we're just mentioning
in the Start function we're saying destroy
the game object
after the max lifetime.
So that's saying when you instantiate this
if it's still alive after 2 seconds, destroy it.
And now we're going to have to write some scripts.
So the first thing we want to do,
when OnTriggerEnter is called, so you remember we
put Is Trigger as True, that means that
OnTriggerEnter is called when something
bumps in to this.
So this function will
happen whenever it intersects with anything
and then we need to check what it's
intersected with and what to do as a result.
So we have this argument
which we've just called other which is of type Collider.
So it's going to say 'okay, we've intersected
with something else, it's a collider, what do we do about it?'
In this case we don't actually care what we're bumping in to
just that it's a collider,
so we should explode.
But what do we do first?
First of all we need to gather up all of the
colliders that we need to affect.
So we need an array of colliders
and you'll remember that's the square brackets after a collider.
And we'll call that array Colliders.
And we're going to set that to the return of
a function called OverlapSphere.
So that's Physics.OverlapSphere
And what that's going to do is similar to a ray cast,
it's going to create an imaginary sphere
and everything that overlaps with that sphere
or is inside the sphere is going to be collected.
And we want that sphere to be at
the shell's location, so transform.position
for it's position.
It's going to have a radius that we've already
got as an ExplosionRadius.
And we're going to use that TankMask
so that it's only picking up the tanks.
Okay, so just a quick note about layers.
Layers we use for various different things
in Unity but ostensibly they're
just for grouping things together in
some way that you want to use
to either include of exclude them from something.
In this case we're using it as a way of
only including or sort of masking out
anything but tanks, which is why we've called this
variable TankMask, because it's essentially a layer on
which only the tanks exist.
But don't think of it in any visual terms,
if anyone's in to Photoshop or anything like that
there's no kind of layering in that sense.
Okay, so now we've got all our colliders
we need to go through them all and
decide what we're going to do.
So for that we need to
loop through them all using a For Loop.
So this is the same format as we saw before
for(int i = 0
i is less than colliders.length
so this is, colliders is the array that we're going
through so it's less than colliders.Length.
And then i++ means that we add to i for every loop.
Then open and close brackets?
Brackets? Yes brackets.
And then we can
use whatever we do in that for loop
to affect each of the colliders.
So the first thing we want to do is check
whether the thing that we've hit has
a rigidbody, so what we do is we
create a rigidbody variable
which we'll call targetRigidbody
and we'll set that equal to colliders[i], so that's
the collider at the position that we're
currently iterating through in the array,
.GetComponent,
so we'll use the angled brackets for rigidbody.
So what that's going to do is
get that collider that we've just gone through
the array and try and find a rigidbody on it.
Next we need to know if it's actually
found a rigidbody or if it's just found nothing.
So what we're going to do is say if
not target(!Rigidbody)
so what that's going to do is say
if there's nothing there then what do I do?
And if there's nothing there we want to continue
on to the next loop, on the the next collider.
You'll remember it's going back to the
start of the for loop and carrying on looking
for any collider that's got a rigidbody
it's not continuing through a list of commands.
So bear in mind that this is a bit of a
safety catch kind of thing because we're making it
everything that's on the Players layer anyway.
So everything should have a collider
and everything that has a collider there will also
have a rigidbody, so we should always find something
but it's good to check just in case because otherwise
you're going to get errors.
So now we know we've got a rigidbody what
we can do is we can add a force to that rigidbody
so targitRigidbody.AddExplosionForce (
and the force we want to add is the
ExplosionForce that we've got as a public variable.
So that's m_ExplosionForce.
Then comma, so we're on to the next parameter.
And the position that that explosion
force is emanating from is transform.position.
So that's position of the shell.
And then the last parameter that we're going to use is
the radius, so how far away
that explosion force can reach.
Okay, so we've added the force now
so the tank is going to move but we still need
to actually cause some damage to it.
So we're going to do something very similar.
We're going to create a TankHealth variable,
which we'll call targetHealth.
So just to qualify that, notice that when we've
made these other variables they've been familiar
components to things that you've
added using the Add Component button.
This time we're making a variable
of type TankHealth.
So that just means an instance of the TankHealth script
on a tank, let's go back from the top.
So we've gathered all the colliders we could find
within the imaginary sphere, the overlap sphere.
And we're looping through
that list, that array,
to the amount that there is, colliders found.
And then with each one we're
checking that there's a rigidbody and if there is
we're adding a force,
and then we want to actually also
take damage on those tanks.
So we're addressing a particular
instance of that TankHealth script so we can
talk to it and reduce health with it.
Okay, so we've got TankHealth targetHealth
and we're going to set that equal to the targetRigidbody.
GetComponent TankHealth.
So we've got the rigidbody, we know that now
because we were able to add a force to it.
So on the same game object as that
get the component TankHealth.
And again we should know that
there's definitely going to be a TankHealth here
but we're going to check just to make sure.
Because if somebody wants to extend the game and they
make something that's on the Players layer that doesn't have TankHealth
then this will still work.
So again instead of if(!rigidbody) we've got
if(!targetHealth) and then continue.
So if there's no targetHealth continue
on to the next iteration.
So a quick note about this line here.
So you'll notice that we've talked about
the fact that we need a reference to this instance
of the TankHealth script.
So you might wondering 'well what's that got to do with
the rigidbody, what's health got to do with physics?'
Nothing at all, the point is that we've found
a particular game object
the fact that we're calling it targetRigidbody
is not hugely important, we'e just referring to
the same game object that we've stored in that.
Often when we talk about rigidbodies
we'll say 'this is a rigidbody object'.
All things in the hierarchy are what we call game objects.
But often what people will say is
'that's a rigidbody object'.
It just means it's a game object with that
particular component attached.
So when we're talking about the target rigidbody
we just mean it's the tank game object that
we know has a rigidbody attached to it.
So we've done that check and we can then
say we know it's the right game object so
we know it's got the TankHealth script on it
and we'll carry on from there.
Just to reiterate what Will was saying there,
you don't need a game object to find
a component on it, you can use
a component reference to find another component.
So that's what's actually happening there.
You've got the rigidbody component, we want to find another
component on the same game object,
that's what we're doing.
Okay so we know that we've got
targetHealth because we've not come out of the loop yet
so let's create the damage,
so float damage is equal to
this CalculateDamage function which you can see at
the bottom of the screen there,
which we will write in a minute,
but what we need to do is give it the
position of the target,
because we need to know how far away is that target
from the shell when it explodes
so we know how much damage it needs to take.
So you can actually get the
position of something from the rigidbody as well.
If you remember back from the TankMovement script
we were doing things relative to the rigidbody's position.
That's the position that we're going to use here.
So we've got the damage
now we need to apply the damage.
So we've got the TargetHealth script,
and we need to call the takeDamage function
that we made earlier.
And we're going to parse in the amount of damage.
So that's all that we need to do for that for loop,
that's all that we need to do for each of the
targets that we've got, but we still need to
make the shell explode
and play the correct audio and stuff.
Yeah, another thing to mention about this is
we're creating a float called Damage,
we've got this CalculateDamage, currently if we play this
nothing would happen because we don't
have anything going on in this CalculateDamage
function just yet.
But all we're doing is just setting it up so when we do
we're going to parse that straight to the TakeDamage function.
If you remember when we looked at that TankHealth earlier
our TakeDamage function there is public,
that's how we're able to just reference it by saying
.TakeDamage.
Public being the accessibility level of that thing.
Okay, we've gone through all the colliders
that we need to do, so now we need to
sort out the particles and the audio.
So when we detonate the shell, when it explodes
we want to remove it from the scene,
we don't want it to hang around.
However if we remove it then the particles
are also going to be removed because they're children
and the audio source is also going to be removed
because that's a child.
So we want to make sure that those
still play even after we've destroyed the shell.
So what we're going to do to do that
is to unparent them.
So it will come in as a complete game object
it'll explode and they will decouple themselves
so the shell explosion will no longer
be part of the shell.
And we're going to do that by setting the
transform's parent to null.
So null just means nothing.
That means it would just be loose in the list
of game objects in the hierarchy.
So we have detached it now,
so next we need to play the
particle effect, so m_ExplosionParticles.Play ();
And likewise we want to play the audio
source so very similarly
m_ExplosionAudio.Play ();
So then we've just got a couple of more things
we've got to do in this function.
We need to destroy the particles and we
need to destroy the shell.
So to destroy the particles we're going to use
the same trick as we did in the Start function,
we're going to give it a delay.
So we're going to say Destroy(m_ExplosionParticles.gameObject
We don't want to destroy the particles because
that will destroy the component.
We want to destroy the game object that those particles are on.
And we're going to do that after
the duration of the particle system.
So you remember before we said that it's going to be
2 seconds, which was a number that decided on.
Well particle systems, one of their properties
is how long they last for.
So we're just going to say destroy it
after how long it lasts for.
Yeah, so not to be confused with the lifetime of
each particle, but the duration is the entire system.
So this, as you can see, autocomplete is showing us here
Optional amount of time before destroying the object.
We can simply just refer to
ExplosionParticles.duration
and that will simply wait for that to finish and then destroy it.
based on that.
Which is great, because you can then go and
adjust the duration and this code will still take
care of it for you.
And as James was saying, the last thing we need to do
once we'd destroyed the explosion, or once
we've triggered a destroy to occur once
it's finished playing the explosion
is actually to remove the shell as well.
So to do that all we do is
Destroy (gameObject) and that will destroy the
game object that this is on.
As ever with scripting everything is just
happening straight away, it's not like we're
waiting for one thing to happen and then something
else to happens, so theoretically you might
consider that you would get rid of the shell and then
the explosion but it really doesn't matter in this instance.
Okay, so, the last thing that we need
to do in this script is complete that
CalculateDamage function.
So in this CalculateDamage function
currently we've just got return 0f and that's
purely just so it doesn't spew errors
while we're working on the rest of the stuff.
So we're going to get rid of that to start off with, we don't need it.
And the next thing we're going to do is
create a vector from the target to the shell.
So that is going to be
Vector3 explosionToTarget.
and that is equal to
the target's position, minus the shell's position,
which is transform.position.
So if you imagine a big circle on the ground,
that's the explosion, at the centre of it is the shell,
and somewhere in that circle is the target.
We're finding a vector between the two.
The next thing we need to do is find out
how long that vector is,
the magnitude of that vector.
So what we're going to do is say float explosionDistance
is equal to explosionToTarget.magnitude.
Okay, so now we've found that distance,
which would be somewhere between 0 and the radius.
We want to know what the relative distance is.
Because we want a number from 0 to 1 really,
rather than a number between 0 and some radius that we found.
So the relative distance
we know the maximum it's going to be is
the radius and the minimum is going to be 0.
So what we need to do is calculate a number that
starts off at 0 and ends up at 1.
We do that by dividing by the explosion radius.
So relative distance is
(m_ExplosionRadius - explosionDistance)
then close the parenthesis
and divide all of that by the ExplosionRadius.
So when you think about that
if the explosion distance is very long,
IE it's close to the explosion radius
then the explosion radius minus something that's
very close to the explosion radius is going to be
very very small, divided by the explosion radius is going
to be very very small so it'll be near zero.
However, if the explosion distance is very small
then the explosion radius minus
something very small, let's say zero
divided by the explosion radius is going to be 1.
So we're going to end up with a number which
starts off at 1 near the centre and
trails down to 0 at the edge.
And that's how we're going to calculate how
much damage is done.
So we create a float called damage.
And that's equal to the relative distance
multiplied by the MaxDamage
m_MaxDamage.
So when the relative distance is 1
and is very close to the centre,
then you're going to deal the maximum damage.
When it's close to the edge, it's close to 0 you're going
to do very little damage.
Okay, so as illustrated in the slide
we have our overlap sphere,
we have our tank right at the very edge of it,
very minimal or maybe 0 damage,
and then something right in the middle of that impact
such damage, many ow.
We're not finished yet with this function.
What we need to do next is make sure
that the damage is not negative.
So we're going to do that by saying damage is
equal to Mathf.max of 0f and Damage.
So what that's doing is saying if it's negative
then set it to 0, otherwise keep it as it is.
And then finally we can return that damage.
Why is it that we're
making sure it's not negative?
Well there is literally an edge
case in this scenario.
In the case that the tank is
right on the very edge it's position
is actually outside of
the overlap sphere but it's collider is within it.
So it's going to get picked up by that collider's array
and then it's going to be calculated in with everything else
but we've only caught the edge of it, so,
we're actually going to calculate a relative distance
that's negative, because you're starting off at 1 in the centre
going down to 0 at the outside
and then it'll be negative beyond that.
So we don't want negative damage,
we don't want to shoot things and make them more healthy,
it's just not the way.
So that's why we're doing that.
Shall we put the script back on and
finish it off?
Okay, so if you've saved your script switch
back to the editor, hopefully there won't be any errors.
And then we need to of course populate
our script with all those public variables.
So interestingly something you won't have seen before,
is the first public variable, it's this
Tank Mask, so remember it's of type
layer mask and we called it Tank Mask.
And what that basically gives you is a repeat of
all of Unity's layer system.
So you'll notice it's the same list that you get in the
Layer drop down at the top.
This Tank Mask
is where we're going to select Players.
It's the only thing we want it to actually effect.
so select the Players layer from the
Tank Mask variable or drop down.
Then Explosion Particles is the child object
that's underneath the shell.
So you can drag a shell explosion on to
Explosion Particles, you'll notice in the
parenthesis it says Particle Systems.
And for Explosion Audio it's the same
object because that also has an audio source.
But this time when you drag it you'll notice it picks that component.
So Unity is smart that way, it knows which component
you're referring to if you drag an object that's got that
component it'll say 'great, okay, that must be what you mean'.
And assign that particular component
from the game object that you drag in.
Then the other stuff that we've setup there,
you know, it's setup based on
something that we wanted to make, if we get to the
end of the day and you want to have a huge
amount of damage, you want to have one shot kills
and you just up the maximum damage
or add the explosion force and the tank
just gets flung all the way off the screen.
You can do that stuff.
Obviously up the explosion radius as well.
You can play around with that.
For now let's stick with these values
and see how we like that.
And then the last thing we're going to do is to save the
shell as a prefab, as I said before we don't want
it to be in the game when it begins.
So I'm going to select my Prefabs folder.
I'm going to drag my shell and drop it in to
the empty space in that folder.
So really we should check if this works, so I'm just going
to briefly save my scene and demonstrate
what you should be seeing.
So if I press Play right now
my shell should fall down
and because it's pretty near the tank
take off quite a lot of damage.
Take off a lot of health, sorry, apply a lot of damage.
We've applied the shell explosion,
in order to populate the particle effect
and the audio source on to those two different variables.
And then we've made it in to a prefab
so now, as mentioned before,
we don't need that shell in the game.
Delete it out of your scene.
Delete on PC, command-backspace on Mac
and then save your scene.
One more time, select the shell.
delete it and then save your scene.
Okay, so that's the end of phase 5.
Then the next phase we're going to deal with
actually shooting these projectiles.
A little bit of physics and a bit more UI.