Following all the set up that we did in the last lecture.
In this one, we're going to get our player shooting.
Now the first step to this is to figure out where we want this shooting behavior to be written.
So if we go into our prefabs and have a look at how our player is set up, we're going to be listening
for On Fire as part of our player input.
And if you remember when we were listening for On Move, we were doing that in the player script.
So let's have a look at the player script just to remind ourselves of how we were doing that.
And if we scroll down, we had this on move event which was listening for the input itself, so we could
also listen for our On Fire event in here as well.
However, we don't really want the actual shooting behavior to be part of this script because our enemies
are also going to need to shoot back at the player.
So let's put our on fire method in here to listen for when the player is pressing the fire key.
And then later on we can use this method to set a variable within our shooter script to start that firing
process.
Before we do that, though, I've noticed we've still got a debug log in here for our on mode method.
So let's delete that first just to clean things up a little bit.
And then underneath this, let's write our on fire method.
So just like the one above it, it's going to be void.
We're going to call it on fire.
And because we change the action type to be of type value, we can pass in a type of input value called
value.
And then in here we're going to be setting a boolean from our other script.
We haven't written that script yet though, so let's save this up for now.
And then on our player prefab, let's go down and add a new component called Shooter.
If we open this up, let's now think about what this script is going to need to do.
Well, the way I'm going to do it is I'm going to have the script instantiate some projectiles whenever
that fire key is pressed.
So to start with, let's clean up our comments, but leave our start and update method alone.
For now, we're going to need a serialized field to hold the prefab for our projectile.
So let's have a game object called Projectile Prefab.
And when we create this projectile, we're going to want it to have a certain amount of speed and we're
going to want it to live for a certain amount of time.
So let's control these variables with serialized fields as well.
We can have a float called projectile speed and I think a default value of around ten should be okay
to start with.
And we need a serialized build again of type float called Projectile Lifetime.
And for this I think maybe 5 seconds should be okay.
Finally, let's actually finish setting up that Boolean flag that we're going to need for our player
script because this needs to be accessed from another script.
Let's go ahead and make it public.
Our public variable is going to be of type ball and we're going to call it is firing.
And now with that set up, let's jump back over to our player script and just quickly finish off our
on fire method.
Before we do that, we're going to need access to our shooter script from within our player script.
So right at the very top, let's add a new variable of type shooter and we'll call it the shooter.
And then if we create an awake method to grab that component off of the object, we can say shooter
equals get component of type shooter.
Now back down in our on fire method.
First of all, we can check whether we've got that shooter script so we can say if shooter does not
equal null.
In other words, did we find it attached?
And if we did, we can set shooter is firing to be equal to the value dot is pressed.
Now that's really all we need to do for our player script.
So let's head back over into our shooter script and have a think about how we're going to start instantiating
our projectiles.
Well, for things like this, I'm quite a big fan of KO routines and we've seen KO routines once, so
this is very good practice to try writing another one.
Before we do that, let's set up the framework that this routine is going to sit in because we're going
to control the starting and stopping of this routine within a fire method.
This fire method is going to be checked every update.
So let's write a dummy method called fire.
And then under this we're going to write that method.
So void fire.
And this is where we're going to start that code routine.
And since this routine is essentially going to allow us to fire continuously, it would make sense to
call this routine fire continuously.
So finally, let's put the bones of that routine in place as well to get rid of our red squiggly.
And if you remember, a coyote has a return type of eye enumerator, and we're going to call this fire
continuously.
With the bones all set up.
We're not going to worry about the red squiggly there for the moment.
That's just because we don't have the return type set up yet.
But before we go ahead and write that coating, let's just think about the logic in our fire method.
The way this is going to work is that if our player script tells us that we are currently firing, then
we want to start this routine.
Otherwise, we want to stop our routine.
To start our coating will be nice and simple.
We're going to say that if is firing so that's controlled by that public boolean, then we want to go
ahead and start our routine.
Else.
We want to stop our car routine.
Now, how do we stop a routine?
Well, there are a couple of options, depending on how brute force you want to be with it.
One option is to just say stop or kill routines.
This will indiscriminately stop any routine that's running.
But it is kind of like taking a hammer to the problem, and we want to be a little bit more surgical.
So the other option is to stop routine.
And then tell it what coating to stop and to tell it which Curry team to stop.
What we're actually going to do is when we start our routine, we're going to store this as a variable.
So this is another really powerful thing that you can do with curry taint.
If we head to the top of our class and create a new variable, this variable is going to be of type
CO routine and we're going to call it our foreign CO routine.
Then we can set this variable by again heading down into fire and where we're starting our routine.
We just want to say that our firing routine is equal to that started routine.
Then with that variable set in our block, we can stop our firing routine.
Now, whilst this will do something, it isn't going to be quite what we want.
So we will have to come back and adjust our if and our else statement, add some extra information in
there.
But before we do that, let's go ahead and actually write our code routine, get some projectiles instantiating
into the world, and then we can see the flaws in this current setup and how we might fix it.
And writing this routine is going to be a challenge for you.
I want you to write the logic for our fire continuously routine, and this chi routine will loop indefinitely.
So think about how to create an infinite loop intentionally.
It will instantiate a projectile for every loop around this infinite loop.
And we also want to set up the destruction of this instantiated projectile after the projectile lifetime
has expired.
And as a hint for this one, check out the overloaded versions of the destroy method that we've used
before.
Finally, we want to wait for the time specified by a variable we're going to call fire rate, and we
haven't actually written this variable yet.
So go ahead and add this one as well.
So quite a big challenge with a lot of moving parts, but pause the video now and give it your very
best shot.
Okay.
Welcome back.
So the first thing I'm going to do is add our missing variable for our delaying of our routine.
We're going to set this as a serialized field so we have some control.
It's going to be of Type Float and we're going to call it the firing rate.
And by default, let's set that to maybe around 0.2.
Then down in our routine, we said we wanted this to loop indefinitely because we are going to be controlling
the starting and stopping of our routine within our fire method.
And to intentionally create an infinite loop, we can say while true.
So inside of our infinite loop, we wanted to create a new projector.
And for this I'm going to instantiate it directly into a temporary variable of type game object.
And I'll call this the instance, and this is going to be equal to instantiate our projectile prefab.
And while I'm here, I'm going to set the position to the transform dot position so it'll be created
at the position of the player's ship.
And because we've specified a position, we also need to specify a rotation and we can specify no rotation
with Quaternions identity.
Now this line is a little long, so let's break it up then.
I said we wanted to destroy this instantiated object after its lifetime had expired so we can use the
destroy method for this.
The thing we want to destroy is our instance and we want to destroy it after the projectile lifetime
has expired.
Finally, we want to yield our routine and wait for the firing rate.
Time to elapse so we can say yield return.
Remember, routines always have to yield rather than just return.
And we want to return a new wait for seconds of the firing rate.
So let's give that a save and jump into unity to test out what happens.
Like I mentioned before, this, if else block isn't quite what we need.
So let's have a look at what actually happens in this circumstance before we go and fix it.
First of all, let's reopen our play prefab and go down to our shooter script.
This needs a projectile prefab.
So let's drag in our projectile player.
Then if we go ahead and hit play to test this out, we can still move around just fine.
We are getting another reference exception, so we'll look at that in just a moment.
And if we try on fire, we get a whole bunch of projectiles all getting spammed out at once and it's
all going a little bit wrong.
So let's come out of play mode.
And before we fix that dodgy car routine, let's just have a look at the console log error and this
is giving us a null reference exception saying that the routine is null.
Now both of these problems are very easily fixed with our if else, block So let's have a look at what's
happening.
What we're doing is when we press the space bar we're setting is firing to true.
And remember, fire is happening in our update loop.
So it's being called every single frame.
So when we hit our space bar, this is set to true.
And then every single frame we're starting a new routine.
The error in the console, on the other hand, comes from our else block in the time before we hit our
space bar.
So right at the beginning of the game, this variable is going to be null.
So we're trying to stop a routine that hasn't been set.
Now, how do we fix both of these problems?
Well, the first step is to look at our if statement.
What we need to say is that if we are firing and our firing co routine equals null.
So if we haven't set this up already, i.e. we've not called start routine yet, then we're going to
start firing.
Once we've done that, once, this statement will be false.
So even if we're continuously holding down the fire key, we won't continue to start more routines.
So that should slow down our firing rate to the correct speed.
And for the second one, what we're going to say is else if because we need to specify some conditions
and we want to specify that we're not currently firing and our firing routine does not equal null.
Now, this sounds fine in principle, but when we stop a routine like this, we're still going to have
something in our firing routine variable.
So to add to our elseif block, once we've stopped our routine, we're going to set our firing protein
equal to null.
So hopefully the logic checks out there and it all makes sense.
Our projectiles aren't moving anywhere though, so let's make that on next priority and we're going
to do this down in our routine.
If you recall, our projectiles do have a rigid body attached.
So what I'm going to do is once we've created the instance of our projectile, we're going to grab that
reference to the rigid body, and then we're going to apply a velocity to it.
So let's start by grabbing that reference to the rigid body, and so we can do a little bit of extra
checking.
We're going to store this in a temporary variable of type rigid body 2D, and we'll call this r b.
Now we want to go ahead and grab the rigid body component off of our object so we can say instant dot,
get component of type, rigid body, 2D.
And for the extra checking that I was talking about, we're just going to say that if our rigid body
are B does not equal null, then we're going to set the rigid body velocity.
And to set our velocity, we need to give it a direction and a speed well for the direction we can use,
transform, dot up.
And if we hover over this, it says it's the green axis of the transform in world space.
So to explain this in real terms, let's jump back over into unity and click on our player.
And here we can see the green up arrow.
So Transform Up is going to follow this green arrow.
So back in our code we can take our transform up, which has a magnitude of one and multiply it by our
projectile speed.
So with that set, let's save that up and jump back into unity one more time.
Let's jump into play mode.
And as if by magic, we now have projectiles shooting at our enemies, and if we hit them enough times,
then we can destroy our enemies as well.
So we've now got a pretty solid game on our hands.
If we hit the enemies, then we die.
But we now have a way of fighting back and scoring some points.
So congratulations on getting the player projectiles working.
And in the next lecture, we're going to look at amending our code to get the enemies firing back at
the player.
So fantastic work and I'll see you in the next lecture.