Welcome back, My favorite game developers.
In this video, we've actually started
spawning enemies in a smart way.
So now when we run the game,
Let's make this bigger so you
can see properly what's happening.
The after the candidate finishes,
the first enemy appears,
the second enemy appears,
and they just sit there waiting for us to kill them.
So once we do kill them,
Let's try to go them.
Okay.
That's actually, oh, okay, that was embarrassing.
Let's restart the level.
Wait for that. So we
made enemies so good that we cannot kill them.
Okay, So let me try.
Let me get to a certain distance so we can no longer.
So once we call them, you can see
that we have the wave complete.
And then again, we get the enemy spawning because we've
made the amount of enemies and here too for this wave.
So let's not waste any more time and let's get started.
Oh, case.
So let's go ahead and actually starts owning enemies.
The first thing that we're going to do is
obviously create in here some kind
of either a co-routine or we can create also a counter.
But we're going to stick with
the Coubertin And this courting will be
called every time we have this spawn delay.
So this core team will be spawning the enemies.
Anyways, let's get started and we'll
see how everything is working.
So we're going to create an eye enumerator in here.
And it's going to be called the spawn wave.
And the wave that we want to actually spawn.
So wave to spot.
So we already have the wave that we want to span.
It will have its own enemies,
its name, and the amount of enemies it has.
So we're going to create a for loop.
In this for loop, we're going to start
with an integer I equals 0.
We're going to make sure
that we never go over the weight to spawn,
or is it the wave to spawn,
the amount of enemies?
And we are also going to be increasing it by
one every single time.
So in here we are going to create a random enemy number.
And it's going to be the random range.
And this range will be over 0.
And the wave to spawn enemies dot length.
So there we go and remember that enemies,
and here is an array of game objects.
So we want to spend a random enemy in there.
So I'm going in here where we'll say
that some kind of method
to spawn because we want to have it
outside the, this right here.
This will only be the spawn wave.
This will be the enemy spawner in here.
And what we'll do,
and the way this numerator will
work as we are going to yield
return new, wait four seconds.
And it's going to be on the wave.
Respond. Not delay, dot spawn delay.
So what's happening in here?
This is the first time I believe that we
use the yield return
inside of a for loop in an enumerator or a coroutine.
So we go through this loop.
Every single time we choose a random number,
we spawned some kind of enemy,
and then we wait four seconds.
And then again, we spawned an enemy, we spawned an enemy.
So this will allow us to
yield return and call this multiple times,
this spawned wave several times.
And after we do this,
we are going to create also
the actual method that will spawn the enemy.
So spawn enemy.
And in here we're going to say that we will send it
the game object and the enemy to spawn.
And in here we're going to say debug dot log just to
make sure that we know which enemy is
being spawned and everything is working correctly.
So spawning the enemy.
And we're going to give it the enemy to spawn.
But name.
There we go. And we are simply coming to instantiate.
And then she ate the NMI. Too small.
And it's going to be, oops, excuse me.
So it's going to be all the transform dot position
and we transform the row station.
So what is the transform?
Which transform are we talking about?
So because the wave spawner is
attached to the level manager,
this will be the position and the rotation
of the level manager game object in our scene.
And later on, we will be creating
several points on which the enemies will be spawning.
So it will work like that.
We will have some kind of array.
We'll deal with that later on that's now
focused on actually instantiating these enemies.
So spawn enemy, the wave to
spawn enemies at the position
of the random enemy number that we just created.
So I'm going to add an entering here. What are we doing?
We need to also call the coroutine and here,
so before we forget, so co-routine.
Spawn, spawn, enemies, wave.
Yep, there we go.
And which way should we be spawning?
While in here we're just going to say that it's the wave,
waves at the position next wave.
So it's going to be probably
0 right now because next wave,
whereas the next wave,
it starts off at 0 right here.
So it's going to be the first wave
that's for just experimenting for now,
that's focused on what this does.
So a co-routine which starts if we
make sure that we are not
spawning of the countdown is less than 0,
we are not spawning,
we are ready to start spawning.
Then we start the coating in here we have the loop will
loop through the amount of enemies on
the wave that we receive.
As a coroutine, we create a random number.
We spawn a random enemy which just has the debug
to tell us which enemy is being spawned
will probably remove this later on.
And then we instantiate the actual enemy on the manager.
And then what happens? We yield return,
wait four seconds and then spawn in other enemy.
So before this loop than again is called,
we have to weigh the spawn delay
that is designated to us.
So I'm going to save all of that.
I'm going to go back and unity.
And let's see what we have in here as a level manager.
Wait for this to compile anything.
Okay, so we go to
the manager's level manager and what do we have in here?
I was playing around,
maybe I did not do this in the video.
So we have this wave right here.
It's the skeleton wave.
The enemies that we have in here are
the skeleton enemy and the cowardly flying high.
The amount of enemies we want is to
and respond lay will be two seconds.
I'm going to save this.
Where's the level manager and our world?
It's not on the 000.
Let's put it back to 0,
so it's right in front of our player.
And let's run the game and see what it looks like.
So now 12, we should see the wave countdown.
There we go and whoa,
that's too much enemies.
Let's see what we did wrong in here.
So we save that.
What's happening in here,
we are checking the countdown is less than 0.
Are we not in the spawning state?
Well, let's see. We start
off with the counting, so we're not.
So obviously we need to change our states in here.
So before we actually create the loop or start the loop,
we are going to say that we are in
a state which is equal to
the spawning States, but spawning.
And then when we finish spawning,
we are going to go into a state,
which is the weighting.
So we're waiting for the enemies to be spawn.
So now we start before we actually go through the loop,
we said the state.
So now this will not be called every single
time and we will not have that many enemies.
Let's again test this out.
Run the game. We can see the countdown in here.
I made it public. There we go.
So 0, we get the enemy and he starts shooting.
Then we get another enemy,
and then we gave them another enemy,
and it's being called every two seconds.
So look at these little cowers right here.
This is a really nice cool game mechanic, as you can see,
every two seconds, 1212, and there we go.
So as you can see,
enemies are being called every single second.
But you now might be wondering,
why do we have so many enemies,
even though we said that the amount
of enemies should be only too.
Well. It's because in here,
we don't actually have the wave countdown resetting.
Again, something that we will deal with later on.
But let's first of all think of when will we be
actually resetting the wave and when does an actual wave?
And so we can either create a time between wave.
So let's say the first wave and the second wave,
there is a delay between them.
Or we can have some really nice mechanic and give
our layer or the person that's playing our game
a chance to go ahead and kill all of
the enemies in this wave before the next wave comes on.
So this is what we are going to do.
And to do that, we are going to
create some kind of method
that will keep track of whether an enemy has died or not.
So let's go back down here.
And after the spawn enemy,
we are going to create a private.
There's going to be a Boolean,
and it's going to be the enemies or alive.
And this will return true if
all the enemies are alive or no.
Oh yes, if all of the enemies are alive and false,
if one of them or none of them are alive.
So in here what I'm going to say,
I'm going to create an if condition.
And I'm going to have the final object of type.
And objects of type actually not find objects,
find up objects of type.
And the enemy controller,
I'll be looking for the enemy controller dot length.
And it's going to be equal to 0.
And if it is, then we are going to return false.
So false, that means that all the enemies have died.
And then if we go through
this one and we don't return false,
then we are going to return true.
That means that the enemies are still alive.
So now we have a wave
or of making sure whether our enemies
have all been depleted and we will be
able to use it right here as a condition somehow.
But there is a small problem that is bothering me.
The find objects of type on
the enemy controller is actually a very cumbersome,
let's say operation on the system.
It takes up a lot of resources every
time to go through all the objects
on the enemy controller,
which is sometimes not very necessary.
So what do we want to do is we want to have some kind
of a timer on the enemies are alive checker.
So we don't actually check
every single frame if all the enemies are dead.
But instead, we have this
being checked every couple of seconds.
So I'm going to issue it to you as a challenge,
and your challenge is to delay checking.
So first of all,
you'll need to create a variable for
the time between checking for enemies.
In the Boolean method makes sure that we don't
look all the enemies except when the time is done.
Pay attention to where we should reset this counter.
So this will be very important.
So with that said,
pause the video right now and go do each Shannon.
Okay, welcome back.
So I'm going to scroll up in here.
And I'm going to create a private float.
The time between enemy search, between enemy search.
And let's say it's one F.
So 1 second. Every 1 second we check
for all of the enemies of the debt.
And then in here I'm going to say that the time between
search is being reduced by Time.deltaTime,
every single frame, and this will
be called every single frame.
And then down here,
I'm going to check before we
actually check for all the objects.
If the time between search is less than or equal to 0,
then we will be searching. There we go.
And then when do we actually reset this?
So once the time between
search is less than or equal to 0,
we start by checking if all of the enemies have died.
And we also can go
ahead and reset this time between searches.
And I'm going to set it to 1.5 because I don't
think that this is something
that you might want to change in the inspector,
the time between enemy searches,
this can be pretty standard.
But if you think that you
might need to change this variable,
obviously this one f will not work.
You need to go up here,
create a separate variable
if you did that, congratulations,
this is the better step or the better way of thinking,
the better game design maybe.
But for me, I believe this
will be something very constant,
something that will not change.
I don't think we will ever have to change
the time between enemy searches.
Will that finished?
Let's go back up in here.
And we need to make sure that we,
Where is it in here and update.
We are going to check
the state is equal to the spawning states dot waiting.
So that means we are constantly
spawning enemies and we are in the waiting.
So we check after we obviously finished this for loop,
we check is the state waiting.
Then if it is in here,
we are going to check if the enemies are alive.
And notice that we added this exclamation mark,
which means the exact opposite.
So if all of the enemies have Lloyd are going to
save debug dot log, wave.
Complete.
Save that.
And else, if it's not,
then we are going to simply return.
And why are we doing this?
Why are we saying return?
We said that returned means that we don't do
anything else inside of the method, especially in update.
Why? Because once the wave is complete,
we don't want to go back into the wave,
can now check if we are not in spawning,
which we will be not n
because we are obviously and the waiting
and start the recording again and
start creating the enemies again.
So once the wave is complete,
we want to stop the whole update from working.
So I'm going to save that.
I'm going to go back into my gain.
And now we should see that when we run the game.
So we get, after five seconds of Countdown,
we get the count the enemy.
After two seconds we can another count the enemy.
And there we go.
You can see that we have stopped everything so we
no longer get more enemies that we have to.
So I'm going to try to kill those.
So we have both of them.
And as you can see, again,
the wave has been completed and then the enemies
start spawning again. Okay, great.
So with that out of the way,
make sure to commit your changes.
I hope you enjoy it and I'll see you in
the next video where we'll be
spawning points for multiple waves
and completing all of them,
killing all the enemies,
completing all the way,
and maybe having some kind of really nice message.
So see you then.