Transcripts
1. Skillshare Introduction: Right, throughout this
course, we're going to go ahead and we're going
to learn to make ease by creating a classic
game from 1979, asteroids. Now with asteroids, you get
this little ship here that can shoot little projectiles and fly around on the screen. And what happens is you shoot these asteroids that are floating around that
randomly spawn. The big ones spawn medium ones, you destroy the medium ones, they spawn smaller ones, smaller ones move faster
than the larger ones. And each one that you destroy
gains you some points towards your total
score. All right. This is a simple game, but is important to both the
history of games as well as a nice little beginner project
where you get to remake an actual game that you can have fun with and always add
on to and make your own. Alright, let's get started.
2. What is Asteroids: All right. Today we're going
to go ahead and we'll be creating the classic
game asteroids, as you're seeing
here on the screen here in this YouTube video. What we can see from
this is that asteroids, aside from being a really
old and classic game. It should be fairly
simple for us, but another step up because now we have player
movement going on, as you can see with our little
player here in the middle. He's rotating, he's
moving, he's shooting. We have these asteroids
floating around, and every time they
get shot, they break off into extra pieces. And we can see we've got
this little alien brill that kind of spawns in
every now and then. And as for UI, we've got points and
health up here at the top. And effectively, this
game just continues on until you either well, I wouldn't say until you either. It continues until you
lose all your lives. And whatever score you have,
that's your high score. And as you can see there, the
asteroid went off screen, and then it appeared back on the other side and continued going in the same
direction it was moving in. But in short, that is asteroids. That's what we are going
to be creating here. So in the next section, we'll go ahead and we'll
start setting up a project, which shouldn't be too much
for us to get ahold of.
3. Project Setup: Let's go ahead and
set up our project. We can come on down. If you're creating this in
separate in its own project, of course, go ahead
and create a new project, name it asteroids. If you're creating inside
of an existing projects, then just come on in
to your file system, right click, create
new, create a folder, and name it asteroids, and we can go ahead and
do a Toti scene. I'm going to rename
mine to say Main. We're going to add a
canvas layer for our Guy, which I will, of course,
rename to be Guy. And what we're going to need is a label for the top right here, and this is going to be our
representation of our score. I'll rename this to
say score label. All right. And I'll
go ahead and save this into my asteroids
folder and save. And really, that's
about all we really need in order for
setting up our project. Most of the work is going to be inside of the player
and the asteroids.
4. Creating the Player: So let's go ahead and create our player and set
up the movement. So you're just going
to come in at the top, click on a new tab to create
a new scene, hit other node. Search for a character
body two D and select it. Now, I already have this
setup right over here. That's just a character
body two D. I'm going to rename it a player, and I'm going to hit Control S, and I'm going to save it
inside of my asteroids folder. Now from here, since I don't have any sprites
to give you for this, what we're going to
do is we're going to create our player sprite since it is so simple
anyway for us, and we're going to create
that with a line two D, since it's mostly
just a triangle with a little notch out
taken out of it. Alright. So numbers I'm
going to use for this. Now, if you wanted to go
online and look around, I'm sure you can
find the sprites, if you wanted to go
about it that way. I'm going to go to the
Line two D and properties. I'm going to tick
closed, turn that on. And I'm going to open
up the points by clicking on packed
vector two array. And I'm going to add until
we have a size of five. My first point is going
to be at ten and zero. My second point is
going to be at negative five and negative six. Index two is going to be zero on the X, negative two for the Y. The three is going to be zero on the X and positive two on the Y. And our fourth index
here is going to be negative five on the
X and six on the Y. Now, that doesn't look like
the sprite we saw earlier. That's because the
width is too big. Now, because I'm using the default size that
I've been using in this, again, I'll open it
up and show you here. Ah, where's this at? Display Window
that's 335 by 255. Now, if you want to
use an authentic size for this, I believe, I think it's 640 by 800 is
the authentic size for this. In which case, you'll
be able to get away with making your ship
a little bigger, right? So, for example, you could pull the XL maybe to 20
instead of ten. And again, you can just
kind of increase solid the. So instead of negative five,
you can do negative ten. So you can play around
with those, and you can deal with a wider line. But since I'm using
this smaller screen, I'm going to change
my width of my line down to one, and we
can see it here. Now, you might wonder,
what about two? Well, we could do two,
but we're going to have a smaller empty space in the middle. And maybe
that's something you like. Go to go ahead and
play that. We can't really see it here
at the moment. I'm going to go ahead
and just drag it in view and play it and you can see it there,
how it looks on the screen. So you might like it like that. But I'm going to go ahead
and change my width to one. I prefer more like
this. It feels a little more authentic to
me, but that's up to you. And again, you can always
change those points. If you want to make it bigger, then you can have a
wider lined width, or you can always go
online and Google around, and I'm sure you'll
find the sprites somewhere for you
to get ahold of. Alright. Now you might notice that we are getting a
little warning here, and that's because we do need a collision shape for us for ourselves that
it's asking for. So we can go ahead and we
can do a collision shape. We can come in, we can pick
a shape. We can do a circle. We can do a rectangle. We can do one of the polygon shapes. Really, it doesn't
matter a whole lot. And if you want to
be even more exact, which would probably
be a little less authentic is a
collision polygon. And then it's the same setup here where you can
just set the points. So you would just set
them just like you did with the players,
for example, ten, here, negative
five, negative six, and you can see it
starts lining up. With our setup. So I just need
to add a couple more here. And it would be
zero, negative two, and one is zero positive two, then negative five and six. And then we would have our
shape all set up for us. Alright, so if you wanted
to go that route, you can, and you can always come in and kind of play with
your points here. No create, but if
you want to hit, click on Edit points,
and then you can just kind of click it
and drag it around. So if you wanted to
edit it that way. Also, if you didn't want
to type in the points, you click on the plus or the Add Points button
up here at the top. And, you can just come in
and create points like that. So, you do have options. I just went with the inside
the inspector because I find it to be easier
because you can just translate your
points directly over. And again, if you wanted to go this route,
you certainly can. It's a little more a
little more exact. However, less I'm going
to drag down a little bit less probably
less accurate to the actual thing in the
grand scheme of it. All right. So however you want
to go about that, is going to be up to you,
whether you want to use a polygon or a shape. Let's get into the
actual movement of our player. So
let's go ahead. Click on player, hit ADScript, add it and make sure
you're adding it into our asteroids folder. And we're going to create
a physics process. And we're going to have a
couple variables for us here. We're going to have a speed variable for how
fast we're moving. We're going to have
a rotation speed for how fast we well, rotate and then our
rotation direction. And before we jump
into this next piece, make sure you go up
into project settings, input map and set
up your controls. I'm just going to use what
I've used in the past year. I'm just going to
use the up down left and right since I've already
got that setup to WASD. Those are the ones
I'm going to use. All right, so let's go
ahead and we'll say rotation direction
equals input dot G axis. Now, we have to put in
the negative action, and then the positive action. These are going to be in the
form of the string names. So this is going to be left. And then right. Because left is going to give us our negative
direction and right, it's going to give us our positive direction
for rotation. And then we're
going to change the velocity to be equal to transform dot X times then
exactly what we just did here, input dot, axis, and same thing. We're going to put in our
negative and positive, so it's going to be
down, followed by up. And then we'll multiply
that by our speed. Now, we're almost set here. We need to do rotation. Plus equals rotation direction times rotation
speed times Delta. All right. And at this point, you should be used to the idea. We have to use either moving
collide or moving slide. I'm going to use move
and slide in this case. And if we go ahead and run this, we should now be able to
move our player around. So if I just hold W, it's
going to go forward. If I hold S, it's
going to go backwards. I I hold A, it's
going to rotate left. I hold D, it's going to rotate right and now we have something
that's a lot similar, a lot more similar to what
we saw inside of asteroids. Now, you can go ahead and
you can tweak those numbers. So you can tweak
speed if you want your ship to move faster
or slower than this. I'm using 100 by default. Rotation speed is using 1.5. You could increase
that to rotate faster or decrease it to rotate slower. That's going to be
completely up to you. But regardless of what
you choose to do, That's just one of the small
things that you're going to decide to make your game feel more just a little
personalized for you. Right? And for a player, that's really all we need to get
ourselves up and going for that up until we
need to start shooting. Alright. So we'll see
you in the next part.
5. Creating the Projectile: Alright, so let's go ahead and create our projectile
for our player. So in my player scene here, I've gone ahead and
added a Marker two D, and this is just so we have a specific location
that we're going to spawn our projectile at that's going to be just a little bit in front of our ship. And the only thing Marker
two D does is gives us a position somewhere
in the world. That's its entire purpose. All right, so you can go
ahead, come up and create a new scene, hit other node, and select the area
two D. And hit Create, that's going to be
our projectile. And in here, I've created
a collision shape, which is just a
rectangle and a sprite two D. For the texture, I've gone ahead and
from the drop down, use new gradient texture two
D. And when you do that, you'll open the first thing up. You'll see this big section. You'll find gradient
in the inspector, open that up, and you'll
have two points like this. One point will be
black by default. So it'll look something like
that. Just go ahead and right click on the point that's in the black
to get rid of it, and now it's just all white. Right. So that's all you
have to do, and then you'll have a
projectile. I'll set up. Go ahead and save that scene as its own so that we can use it. And go ahead and
give the area two D. I've named my
projectile a script. And here we're going to have a two functions and a variable. All right. The first
variable we're going to have here is we're going to
create a velocity for this. It's a vector two,
and of course, it's going to be set to
vector 20 by default. We're going to create
a function for this called set velocity. It's going to have one
argument of a vector two. It returns nothing, so avoid. And all we're doing
is going to set velocity is equal to V, right? The argument that we pass in. And then last, we need
the actual movement to happen for our projectile. So in our physics process, we'll go ahead and
do position plus equals our velocity times Delta. And our projectile
itself is done. We now have to head
over to player. And if you don't have an
input mapping setup already, come on up to our project
settings input map and create a button
for you to shoot with. Myself, I have my
space bar setup. And before we continue, first
thing I'm going to do is create a new variable
called projectile. So it'll be a packed scene
that we can bring in. And this way, we can
load it in when we need it or create an instance of it when we need
it, I should say. And that'll be equal to load and just find that
scene and drag it in. There we go. So we're
going to load that scene. It'll be loaded as a pack scene and stored in the variable
called projectile. All right. So let's
go ahead and create the input function.
For our player. This is just funk
underscore input. This has one argument
called event. It is an input event type. It returns nothing so void. And we're just going to check if input is action just press, so we can't hold the button now. We have to press it
every time we want. And the action we're
looking for is shoot. So whenever we hit
the shoot button, what are we going to do? Well, we're going to create an instance
of our projectile. So PI for projectile instance, it's an area two D. That's equal to projectile
dot instantiate. Now we need to use that
Marker two D for us here. So PI Global position is equal to dollar sign Marker
two D. So we're just getting that Marker
two D no that we added into our player
dot global position. So we're setting
that up. And if we just take a look at
that the way it is, if we go around and we shoot. Now, we might not really
be able to see it, but if we turn and move away, uh, well, we can see we're not actually getting
anything. Why is that? Because we haven't added
it to the scene yet. It does exist, though, and we'll check that
in just a second. What I'm going to do
is I'm going to set the rotation I set the rotation now based
off of our ship. So PI dot rotation
equals rotation. And I'm going to use
Get parent Ad child PI. So I'm using Get parent. That way, the
projectile does not stick to us because if
we just use ADCild, it will stick to us, and if
we keep turning our ship, then it's going to keep turning. This This would be useful for something
like a laser beam, but not for a projectile. So we're using get parent
so we can stick it to the game and not the
player itself. Right? So if we come in and shoot, you can see it is now
spawned into the game, it's just not moving anywhere. And this will happen every
time we hit our shoot button. So if you got this happening,
then congratulations. Your projectile is working. We just need to get it to
actually be moving for us now. All right. So check if it or
to get it moving, we're going to run a
quick I check here. We're going to say, I PI
has methods set velocity, and we could safely assume this. But we're going to make
sure that it does have this function just to be safe. And we're going to
save our direction. But we're creating a
new direction here. Is equal to vector
two dot right, dot rotated and pass
in the rotation. So this is going
to get us whatever the right direction is, which would be on the
positive X based off of how much it's rotated
effectively, right? So this way we always have a specific direction
it's moving. Whoops. Whoops, whoops,
whoops. There we go. We're going to give it a speeds a var speed equals to 200. You can always tweak this
later if you would like. And now we have to do is call that function that
we check for here. So pi dot set velocity,
direction times speed. And with that, our projectile now move in whatever
direction we're shooting. Just like that? Even if we
stop moving altogether. Great. Now we have our projectile
working with our ship. Now we just need
some asteroids to shoot and some points to gather. Alright. I'll see you
guys in the next one.
6. Creating the Asteroids: So let's go ahead and
create our three asteroids. We're going to create a large, a medium, and a small. These are all going to
be created the same way. The only difference is
going to be the size of the collision shape and the actual point numbers
because obviously, they're going to start
large and they're going to get smaller
and smaller. Now, all of these
are created through a character body two D, a line two D, and
a collision shape. So I'm just going to give
you the points for line two D. So go ahead and pause and when
you've got that setup, then we can go
ahead and advance. I'm going to go ahead
and start now by reading out the
points for the line two D on the large asteroid, remember to save your scenes. Closed, should we set to on. And the width I'm
using on these is two. All of these have eight points. And these are going to starting with the large asteroid here, 40, zero, 30, negative 25, ten negative 35, negative 15, negative 30, negative 35, negative ten, negative 30, 20, negative ten,
35, 25, and 30. Moving over to the
media mastoid. I've got again, eight
points going down the list, 25, zero, 20, negative 15, five negative 22, negative ten, negative 18, negative 20, negative five, negative 18, 12, negative five, 22, 15, 18. Moving on to the points
for the small asteroid, 15, zero, 12, negative eight, three, negative 13,
negative seven, negative ten, negative 13, negative two, negative 12, seven, negative
three, 13, and 1010. Those are the points that
we're using for all of these. And once you have all
three of those set up, make sure you've saved
all three scenes and you'll have your asteroids
set up and ready to go. And the only thing they'll be
missing is their rotation. Of course, they need
to be spawned in and spawning the smaller asteroid
when they get destroyed. So the large ones will
spawn medium ones, and medium ones will
spawn small ones.
7. Asteroid Spawning: So let's go ahead and
allow our asteroids to spawn off screen,
move into view. They're going to be able
to have different speeds, and we're going to
use the same script on all three sizes. So go ahead and create a script, and you can attach it to all
three of your asteroids, the large, medium and small, and open up that script. And the first thing we're going to do is we're going to create a rotation speed and I'll
create an X four speed. I'll go ahead and
save that right now. So if I click on
my small asteroid, you'll see we have speed
here in the inspector, I'm going to leave
that at 100 for small. For medium, I'll
turn it down to 50, and for large, I'll make it 20. Now, you can always
come back and tweak these numbers later, right? That's always possible. I'm just going to go ahead and
set that for now. Now, inside of our
ready function, in order to spawn off screen, what we're going to do
is we're going to get the screen size from
Get VportRc dot SHE. And we're going to get a credit
variable for spawn edge. Everyone s Randy percent four, so that's going to
get us either a zero, one, two, or three. And based off of this,
we're going to determine whether we're going to assign each number or
associate each number to be top, bottom,
left and right. So we're going to use something
called a match statement. And all you do is type in match. The thing you want to
match, in this case, would be spawn edge. Colon come down, and we'll say zero because
that's an option. Colon drop in, and you
have a block of text. Then you can match
it with a one, and a block of text, two, and a block of text, and so on. That's how we match
statement works. So we're basically saying, Okay, we're going to match this. So if this matches
this, do this, it matches this,
we do this block, if it matches this, we do
this block, and so on. So what we're going
to do with that, I'm just going to go ahead
and copy this in of course. But we're going on all of these, we're going to go spawn edge. If it's zero, we're
going to take its position is equal
to a vector two, and that vector two is
a random float range, so rand F onto square range between zero and
screen size dot X, and then the Y is negative 50. So since it's negative you can assume here that
the position here, that the match of
zero is going to be up or the top screen. So if we're spawning
above our screen, this is where we're going
to place our asteroid. And then our velocity is going
to be vector 20 on the X, and then our Y is just going
to be our speed, right? So with that being
a positive number, that means we're going
to be moving down and we're going to continue
this pattern all the way. So if it's a one,
position is going to be a vector two of screen
size dot X plus 50. So that means we're spawning on the right side of
our screen here, Rand F underscore range, and that's going to
give us between zero and screen size dot Y. So we're spawning on
the right hand side at any point vertically, as long as we're on the right
hand side of our screen. Then our velocity is
going to be a vector two. It's got a zero on the Y and
a negative speed on the X, so that is moving to
the left. All right. And if we match with a two, we're basically going to do the same thing that we
did with zero here. So our position is a vector two, rand F on the square range,
zero, screensizet X. So we're on the far
left side right now. Screensizet Y plus 50. Since we're adding it, we're
spawning at the bottom, right, bottom of our screen. Vector two X is zero
for our velocity, and it's negative
speed, which means our asteroid is going
to be moving up. And last but not least, we
have a match for three. Vector two is going
to be a negative 50 because negative 50 is already going to
be off screen as it is, so it's off to the left. Random F range of zero, and of course,
screen size dot Y, anywhere on the left hand side, and the velocity is going to
be positive speeds that it moves to the right
and zero on the Y. All right. So if
you have that we actually need to do one more
thing because we can't move, and you might have noticed it is we have to go into
our physics process, and we need to call
move and slide. And I'm also going
to add rotation plus equals our rotation
speed times Delta. If we test that, you're on my small asteroid.
Well, there we go. We saw it across the
top. I'll run it again. No, I came from the
right hand side this time. I'll try it again. I came from the right
hand side again. So we can see it's always
completely random, came from the bottom, and
it's consistently working. And if we move over to say, my large asteroid where
I've made the speed 20 and I test it,
again, it's rotating. It's coming from a
random direction, and it's just moving slower
because we adjust the speed. Alright, so with that, it is now working
perfectly fine for us. Our asteroids can
spawn and rotate, and they all have
different speeds. We now have to get to the shooting and destroying
section so that they can spawn smaller
pieces of them.
8. Asteroid Destruction: Let's go ahead and
we're going to set up our projectile to be able to destroy our asteroids so that
it can spawn a smaller one. So the large ones are
going to spawn medium, mediums will spawn smalls. So the first thing we need
to do is we need to go to our projectile scene,
select our projectile, the area two D, look for
the collision section under collision object two D
and set the mask to two. So layer is the layer
that the object is on, and mask is basically the
layer it can interact with. So we're going to
set mask to two, and then we're going to go
to each of our asteroids. And select it and
set our layer on the asteroid to two
and our mask to one. So this way with mask
being one on the asteroid, it can interact with our projectile and with
it being on layer two. And the mask two
is not selected. The asteroids should not
interact with each other, so they should go through
each other and not smack each other. All right. And you can do the same
thing on the medium and the small asteroid. All right. In my main scene, I just put I moved my player into it
and I just pulled in two my medium asteroids for this for us to use
as testing here. And if we were to play it,
we asteroids are spawning, they come from
different direction. In this case, they both
came from the right. And there we had one from the
right, one from the bottom. Oh, and you can see we didn't kind of smack
into it there, which is fine because when
it comes to it later, that's going to end up
killing our player. So it doesn't matter that we can interact with it right now. All right, so how do we get
our asteroids to work up? Well, first, let's go
to our projectile. Let's look to the node
tab at the signals. And we're going to
select body entered. And what this is going to
do we connect the signal, just double click it and
connect to projectile. This is going to go off anytime a physics body
touches our asteroid, because it's a
character body two D enters our projectiles area. So body is going to represent whatever has entered this area. So what we can easily
do is say, I asteroid. Wow, I really
messed that one up. I asteroid and body dot name. Because remember,
mine are all large asteroid medium asteroid,
small asteroid. So if asteroid is
in body dot name, then we must be
hitting the asteroid. I'm going to do body
dot D asteroid, which the custom function we're going to create
here in a moment. And then I'm going
to do self.q3. That way, my projectile
can destroy itself. Alright. Now, if we
were to run it now, of course, this
isn't going to work. We're gonna come into an
error and somebody like, Hey, destroy doesn't exist. Boom. There we go. Oh, and I haven't
pulled this out yet. So I guess that's a good thing. So what I'm going to
do is we're going to go to asteroid script, and I'm going to create a
variable here called New spawn. This is going to be a bull, which is a true or false. And by default, I'm going
to set this to true. I'm going to go down to that
match statement that we had, and right above it, I'm going to say I, new spawn, and then I'm going to grab
that entire match statement. So from line 13 to 25, highlight them all and
hit tab to move it in. And outside of that if
statement, I'm going to say se. And what I'm going to
do is I'm actually going to select that
match statement, and I'm going to paste
it inside of my se. But what I'm going to
do is I'm going to delete the position changes. So I have all I'm modifying in these matches are the velocity because we don't want
to change the position. We just want it to have a random direction
that it's going. All right. Now for
that destroy function. Now for that destroy function, what we're going to
do is, in this case, I have a variable called
ST, and I'm loading, in this case, my small
asteroid because we're testing with the
medium one right now. And I want to spawn two of them, so I'm running a four loop. So four I in range
of two, right? So we're going to run
through this twice. All right. So running
into this fore loop, we're going to create an
instance of our small asteroid. We're going to set the
global position of our instanced asteroid to whatever our current
asteroids global position is. We're going to set
new spawn to false. That way we don't run into our match statement
to change the position. We only run into it
to set the velocity. And we called GparenttAd Child
to add it into the scene. And once we've run
through that twice, then our big asteroid is
going to cue for itself. Now if we run that
and give it a test, boom, we can see it works
perfectly fine here. And if you're curious about all those red errors down
there in the debugger, it's just telling
us that we should use call deferred here during our on body enter for what we call
body dot destroy. So instead of body dot destroy, what we do is we do body call deferred and pass in
Destroy as a screen. And now if you run
that, you'll see all those debuggers
just disappear. Alright. Perfect. Now we have it
destroying and spawning. The medium ones, sorry, the medium ones are destroying and spawning these small ones. So now we need a way
for our asteroid to know what it should be spawning or which version it should be spawning
and what it is. So if it's medium, it
should spawn small, and if it's small, it
should just destroy itself. If it's large, it
should spawn to medium. So how can we go about this? Well, go up to the top. Let's create ourselves
a new variable. Well, to start off, we'll
create an enum called the eyes, and this is going to
have large medium small. We're going to have an export,
so we can change this. So export of our size. And the type here is going to be size from our Enum
so all cap size. And we can just
leave that blank. If we come in onto any of our asteroids and in the
inspector, we should see it. So make sure large
is set to large. Medium is set to medium, and the small one
is set to small. If I check my main scene, these should automatically
be set, but they are not, so I'm just going to delete them and bring in
medium one as well. Probably because I
just didn't save them. Oh well, not a problem. I'll just bring in two
medium ones anyway. All right? Off our
asteroid script, we can now come on down. And inside of our destroy, we can go ahead and
do a match, right? So we can say match size. It's going to be a sized large There you go. We're going to have
a size dot medium, and of course, a size dot small. So this is what we're going
to do on all of these. Now, of course, we can basically just copy
everything that we have here, cut it out, and basically paste that inside of our medium. Because this is all
of our code for that. Yeah. So if we were to test this with our
medium asteroid here, it should work exactly the same. Fantastic. We did only
spawn two of them, though. I'm sorry, one, which
is interesting here. No, there we go. There's
two. And I'll test it again. There's two and two. Okay, so it is working, right? So that's working perfectly. Now what I'm going to
do is I'm going to copy this and paste it
inside of my large. Only obviously, instead of loading the small
asteroid scene, I'm going to drag in my
medium asteroid scene. So now if we destroy
a large one, it should spawn a medium,
and we destroy the medium. It should spawn a small. So let's go ahead
and take a look. I'll delete both of
those out of my scene, and I'll drag in
a large asteroid. And let's test it.
There's a large medio, he left when he had
to come towards us. All right. Time here, store the large. We get a medium, store the
medium, we got a small. Alright, so that's
working perfectly fine. Awesome. Now for
the small, yeah, we don't really need to do anything but just
kind of pass on it because that just
needs to destroy itself. At the end of the
day, we don't need to spawn anything else. But for now, we can leave it like this because we will use this for updating
our points later. So we do still need to have
our small section here. So now we have our asteroids that not only spawn in
different directions, but we can also destroy them, and they spawn the
next thighs smaller. Now would be a good
time to go in and test it and decide if
you want to maybe change up the speed numbers a bit because maybe they move a little too
fast for your liking. There we go. We have our
asteroid destruction set
9. Spawn Timer and Memory Leaks: Let's set up the full spawning because now that we have
the destruction working, we have our asteroids spawning correctly out of
the screen area. So let's have it so that it's continuously spawning for us. So what I'm going to do is I'm going to go to
my main scene here. I'm going to do plus or add a node. I'm
going to add a timer. I want to call this spawn timer. Okay. And this spawner is going
to have a wait time of m. Sure. Let's do 2 seconds.
Maybe we'll change it. We can always again
tweak it later. This gonna start automatically. And I'm going to
go ahead and add a script to my main note here. And I'm going to
connect a signal, so I'm going to
click on my timer, go to the node tab,
double click timeout. Make sure Mina is
selected, hit Connect. And when it connects, we're
going to get a random number, just like we did with our sides. We're going to create a
variable called asteroid equals Randy and percent three, so we're going to get
zero, one or two, and we can match asteroid 01 and two. And what we're going to do is I'm just going to click
into my asteroid script here because we can actually
take a piece of this. And it's going to be this
long set that we have here. Base off of this,
we're either going to spawn the small asteroid,
large or medium. So for zero, I'm going
to set that as my large. There it is. So one is going to be my medium, and three can be my small. Then all I'm going
to do is just do add Child and just do
ast dot instantiate. And that should work on all
of these lines. All right. So basically going to pick
a random number to spawn a random asteroid and then
either load up our large, medium or small, and then
add it to the scene. All right. Let's
go take a look and see if that works
every 2 seconds. Boom, boom. Well, oh, here we go. Why can not be destroyed.
That is curious. Okay, so the small ones looks like we have an
error, some of these. The issue with that is
if we were to run this, and if we click on the
remote tab, right, you can see all the
asteroids that are coming and we take a look, and we see some of them are
just getting assigned like character body two
D, for example. So it's not always running because obviously
they don't have asteroid in their name if they get reassigned a generic name like this, as you can see here. So, what we actually need to do is we need to go
inside of our script. Let's go to our projectile. Instead of using if
asteroid and body dot name. Let's do I Body dot
has method Destroy. Now, if it has the
destroyed method, then we're going to call it. And that one change
should completely fix this for us. All right. There we go. Right now we don't have any
issues with this. Oh, detection. No, it's going. It's going. Now, you can see
that we are getting an unlimited amount
of asteroids here, so we are creating a
bit of memory leak. So we do need to clean
that up as well. So in our asteroid scenes, let's go ahead to all
three of them and add a visible on screen
Notifier two dende. We're going to go over and we'll have the TreenEited signal. And we can connect that to our asteroid script, hit Connect. And all we're going to do
in that is do self cute free and save it and we can
go over to medium again. Screen exited, connect it. I should have the
same name by default, so we should have one function covering all three of these and small at the node,
screen exited, connect. So what this is going to do
is this is basically going to say when the
asteroid goes off screen, then destroy. Alright. So now if we were to
take a look at this, and I'll click on
remote, so we can see. Who let's actually
go to the main scene to play and click on remote. You can take a look. And we can see when
they go off screen, they automatically
get destroyed, so we're not creating
this infinite memory leak for ourselves. All right, so you
can see the game is cleaning up after
itself, just fine. Alright, fantastic. Now, what we're seeing here is we have a similar issue, right? We see all these area two Ds. These are our projectiles. So we should probably
clean up those as well. So we can do a
similar thing, right? We can go to our projectile, add a visible on screen
Notifier two D, screen exited, connect to
our projectile script, and just call Qqare. So if we don't shoot
anything and we miss when our projectile goes off screen, that should
clean itself up. Let's go over to
remote, take a look. And we should see all these projectiles
delete themselves. So we got everything
nice and cleaned out. Everything keeps
deleting itself. All right. Fantastic. All that's
left is we need to update our score with giving ourselves
some points every time we destroy an asteroid. All right. And, of course, then
we need to take a look at our player death, right? Losing our lives for the player. All right. So we can start
tackling that next time.
10. Update Score: Let's go ahead and update our score whenever we
distore our asteroids. Now, we could use things like
get parent and get child, then call methods on that, but a better way to do this is to actually use
signals, right? So these things here that
we have on the side, only we're going to create our
own custom signal and send it to the Guy to tell it to
update the score for us, and we'll tell it
how many points. Now, firstly, make sure your
Guy has a script on it. Mine's completely empty. And then we can head over
to our asteroid script, and we're going to create a new exported variable
because we need a new each asteroid has a
different amount of points, and coincidentally
these actually line up with the speed that
I have set up. So if we just go through, large asteroids in
the game, I believe, give 20 points, the
medium ones give 50, and the small ones give 100. So coincidentally it is exactly the same numbers that I gave for my speed
for all of these. And the next variable we're going to need here is actually going to be a signal. So we type in the word signal, and then the name of the signal. So I'm going to say destroyed. And I'm going to use open and closed parentheses just like a function because I
want to add a parameter. I'm going to call this PTS
and cast it as an int. So if we were now to look at our asteroid and go and
take a look at the signals, we can see our custom one
here called destroyed, and it has an argument
called points site PTS, that is an integer that gets
passed in with the signal. So for us, whenever
we send this signal, we're going to send this
points variable with it. So first, we need to
actually connect our signal, so I'm going to go to my
main scene here for a second and load up
my asteroid script. So we're going to do Destroyed because that's the
name of the signal. Make sure you get the one
with the signal icon here, the one that looks
like a Wi Fi symbol. I do dot connect. Now this takes a callable, and all this is saying is who we're connecting to and
what function we're calling. So we can just say callable
open and close parentheses. The first argument is what
we want to attach to. So this is our parent yet child and pass in Index
is zero when getting child. Coma the name of the function
that you want to call. So for me, I'm just going
to call it Update SCORE. And if you're confused
on how we got Get parent and get child zero, if we take a look,
we have an asteroid in here as an example
in our scene. If we follow this line,
we do get parent. That takes us up one branch. So up one and up that
takes us to Maine. And we want to get
to Guy from Main. So for that we have
to get a child, and that's going to be
its first child, right? It's first branch up there. So from the large asteroid, we get parent to go up and then get child
to come back down. So we're connecting to the Guy. And when that signal emits, we're going to look for a
function called Update score. All right. Now, I'm going to
copy that entire line because we're going
to do something a little similar down here when we come down to
our destroy function. Only instead of connect, I'm going to call disconnect. Right? That's the
only thing I'd change is we're just adding three
more letters, right? So disconnect instead of connect inside of our destroy function. This way, we can just kind
of clean up the signal. We shouldn't have to, and you'll probably be fine
without doing this, but just for a little bit
of a better practice, I'm going to go ahead and
just throw it in there. Now, when it gets destroyed, we also need to emit our signal because we
haven't done that yet. So before we
disconnect the signal, we're going to do
destroyed, emit, and we need to pass
in the argument, which in our case, it's expecting a number
to get passed in. So we're going to pass
in our points variable. All right, go ahead
and save that. So now if we go over to our
GUI, actually, you know, at first, let's go
ahead and play it, and you'll see we'll
have an issue. Right? Well, it looks like our issue isn't actually
going to show itself, which is a little interesting, but nothing's going to happen because that
function doesn't exist. Eustly no function is
being called here. So if we head on over into our Guy and we
create this function, so I believe I called an
update score like that, and we have one argument, right? Points, so I'm just
going to go PTS, and that is an integer. And we can go ahead
and we can just print that PTS that
it's passed in. And if we run that we should see all of our points getting printed out every
time we destroy one. There we go. We've
got 100 points there. 20, 50, 100, 50 right, so it looks like this
is all working fine. We just need to actually
update it visually. But now that we know
this is working, we need to keep
track of our points, and we need to update
the actual label. So I'm going to create a
variable here in our Guy. This is going to be our score. This of course, will be an
integer and start at zero. So the first thing we're going
to do is we're going to do score plus equals PTS. That's why we can add
the points to our score. And then we need to actually
update the label itself. So let's go ahead and we can use our dollar sign here and
get our score label. Dot text. And we
can set that equal to a string of our score. Now, this is going to
look a little weird. If you take a look at the
top left at our points. Come on, spin around
a little hippy. And you see all those
zeros disappeared, and the numbers kind of
change on their own, of course, but you see they're
constantly growing, right. So when we get to 1,000, we're
just going to have another random another random one here. Should I get that? There we go. So we've grown out
an extra number. And you might like that. I don't, especially when I'm doing a classic game like this. So what I'm actually going
to do on the end of this. So when we convert this
score to a string, I'm going to do Dot pad zeros, and I'm going to put
a number in there. Now, if I were to put in nine, for example, we take a look. We can go ahead and
update the score. Hey, go ahead and bonk me. That was rude. Boom, you can see we have all these
zeros by deva, right? And every time we update it, it looks a little nicer, especially on a retro system, especially when you have a max score as well
that could be attained. All right. Now, I'm not 100% sure what the
maximum score is, but This does always look nicer when you
pad out the zero. So I just went
ahead and checked, and the maximum score is 99,990, so that's going to
give us one, two, three, four, five, so
that gives us five slots. So let's go ahead and turn this down, and
we'll check that. Double check those numbers. If we pad it with five,
that was a quick one. We just need to get something on the board here. There we go. And that would give us
99,990 as a possible max. All right, so there we go. So if you want to be authentic, pad it with five zeros. There we go. And there
goes our little score. Little at it go. Peep peep. And Bank, that would have been a lost life for us,
but there we go. Whoops. Still holding
the A button. But there we go. We
have our score updating every time we destroy
our asteroids.
11. Update Lives: Early. So we're
going to take a look at adding health, right? So we're going to add player
lives here to our game. I know. And since we don't
have sprites to use here, we went ahead and drew
out our player character. We're gonna have to
do things a little different, but that's fine. It won't be too
difficult for us. The first thing I want to do
is inside of my Goey here. Because things are going
to change a little bit. I'm going to go
inside my Guy script. I'm going to create an export, and this is going to be
my score label variable. This is going to
be a type label. And I'll just replace
this in my code so I don't have to grab
that variable. So I can click on Guy and I'll see score label in my inspector. Click on that and I can assign
it my score label node. Alright. Now, I did that because I think
I'm going to move this around a little bit when
we're setting up our lives. So by doing it this way, we shouldn't have to
update it in our code. All right, so I'm
going to go to my Buy. I'm going to add
a VBox container. Now, a VBox just allows us to vertically
stack different things. So, for example, if
I add in a label, add in whatever text, and you'll see if I
keep duplicating that. You'll see it's all going to be perfectly spaced out vertically, all nice and neat for us, right? And all evenly spaced. So a VBox is really nice. Alright, so I'm going
to go ahead and take the score label and stick
it inside of the VBox. All right. And if we were to take
a look at the Guy, we can see the score
label is still set. And because that we don't
have to edit our code. And if we go ahead and test it, we can see that ourselves. There's one all the way
down there, and we can see our score and everything
still works perfectly fine. We don't need to
update anything. So it's nice using exports
like that at times, especially when
you don't know if you're going to be moving things around when designing
your I like this. All right. So the
next thing I need to do is inside of my VBox, I need to create an H box. Now, an HBox works the exact
same way, HBox container, only it's for things
going horizontally, side to side instead of
up and down vertically. And inside of this HBox, I'm going to add a control. Actually, I'm not going
to add the control yet because I'm going
to show you why. So I'm going to
click on my player. I'm going to grab the line two D where we're drawing
our spaceship. I'm going to control
C and copy that. Go back to my main scene and inside of the Sage
box container. I'm going to hit Control
V to copy it in. And you'll notice Whoops. If we just have a
bunch of these, nothing is getting organized horizontally like you
saw with the V box. And that's because it has to
be a control type, right? So it has to be a green node. So that's why we're
actually going to come in and inside of Ace box, we're going to add
a regular control, and I'm just going to give
that control a size here, one lower H box actually
needs one, too. But my control, I'm going
to go ahead and set it's custom min here to be 15 by 20. I don't know. That might be a little munch.
I'm going to take my line two D here and just pull it down and stick it
inside of control. Double. All right. So just so I can see
things a little clear, I'm going to select my control. I'm going to turn on clip
contents in the inspector, and now I'm going to
select my line two D, and that's going to allow me to move it in a little better, find out where those limits are. And I'm going to go
down to my rotation, and I'm going to put -90 degrees just so I
can rotate it up, and I can see I need
to move it down more. Whoops, down one right there. Alright. Now I can turn
clip contents off here. And I can see my Y is
a little too much, so maybe 15 there as well. That might be a little short. 16. Once I'm happy with that. Before I'm putting it, I can
take a look at my H box, and if I just copy
this control now, I'm going to select
it and do Control D. We can see now because
it's inside of a control, they're getting all
nice and evenly spaced. O so if we go ahead and play it, we can see it looks
nice up there, right? Just like our ship, and
it's part of our UI. So since we didn't have
any sprite to go up there, we're just taking
advantage of the line two D that we already
used to draw our ship. And now that we have
our ship working, and you see it's going to
be in front of us here, just like the rest of
the UI, just as a shod. So now we need the
actual lives working. So we need to subtract lives whenever we
get hit by one of these asteroids instead of being able to push
it around like this. For that, our player needs live. So let's go to my player script. And do you want to
put our lives there? No, you know, I think I'm
going to put it my Gui. And I'm also going to create
another export for my HBox. I'm going to say export
of our Lives container, and that's going to be an HBox. And I'm going to create another
variable here for Lives, which of course is
going to be an int, and the starting
amount here is three. And we'll go ahead and create another function
called Update Lives. Alright, and we'll
just pass for now. All right. So the question is, how are we going to detect when we run into the player or when the
player runs into an asteroid? To do the collision to subtract from our lives
and pass up our signals, let's head over to our asteroid and inside of our
physics process. We're going to check if our asteroid is
colliding with anything. So say bar collision count
equals slide, collision count. All right? So we're
going to see if we're colliding with anything,
and if so, right? So we'll say if collision count, which means we're colliding with at least one item in order
for this to be true, right? It's the same thing of
saying if collision count greater than zero, right? It's the same thing, less words. So if collision count, then we're going to
get the collision, and we're going to assign
that to slide collision, and we're going to pass in index zero so we can get the first
item we're colliding with. Then we're going to get
the collider itself. So far, collider equals
collision dot et collider. And if we were to just print
this out to see what we have, here we go. Let's go ahead and run this, and we'll see when our asteroids show up when we run
into it with our ship. We can see a character body two D is being hit and
it has the name player. So we are definitely detecting when the player
gets hit by the asteroids. Alright. So so far so good. We know we're hitting
something and that something is our player. So in this case, it
has to be the player. There's no way it isn't. But to be safe, again,
we could be like, I collider dot name
is equal to players. So if we just want to
make that double check, we could do something like this. And we've already checked that
it is hitting the players. What we can do here is
self dot free, right? So we can destroy the
asteroid when we run into it. And we can test that working.
Always test your code. B, p, p, p, boom. Alright, so so far so good. Every time we get
smack an asteroid, the asteroid gets destroyed. Boom. And we can still
shoot and destroy them. All that still works. But
boom, boom. All right. Now, what we also want
to do here is we need to emit a signal here, right? Because we need this
signal to go with updating our lives from the
player and the asteroid. So we need a new signal
from our asteroid here. So we're going to say
signal, asteroid hit. And we don't need to pass
in any arguments for this. We just need to know that
the asteroid has been hit. So we're going to take
that, come on down. And once we connect with the player before we
u free ourselves, we're going to emit this signal. So asteroid hit dot emit. And then we can disconnect. Asteroid hit disconnect. Now I'm aware we haven't
connected it yet, but we are going to here. And this is actually
going to be very similar to our connect. So I'm just going to
go ahead and copy the argument there
from the callable. So we're going to
connect to the GUI and pass in this function
to disconnect from. And in this case, mine
is called Update Lives. All right, so I'll pass that in. Well, it looks like I'm
missing a parenthesis here. Let's see. You're
part of that set, you're part of that set. And I have one too many. There we go. All right. So I'll copy that whole line. I'll move up to my
ready function. And just like the
destroyed signal, I'll connect this one. So connect instead
of disconnect. There we go. Then we have two signals that
we're connecting and ready, and one that we emit and disconnect when
we get destroyed, and one that we emit and disconnect
when we hit the player. Alright, so now we
need this function to actually do something. So in our Gooey script, we can take a look at this
and we update our lives. So we can do lives
minus Sequals one. And then we need to
update our lives label. Do I need to update that? Because I changed
the name? I do. Go because I had
a typo in there. So I'm going to get my
Lives Container dot get children or Get Child Count. What do we want to go
with? I'm basically going to set this with what
we want to remove. We do down to two. So I'm going to do Lives
Container dot, get child. I'm going to pass in
our Lives count here. So it's going to start at three, and then once we get
hit, that'll become two. So two is going to grab our
newest our latest one here, latest life because
we only have three, and it comes up at index 01, two, and I'm going
to call Dot Hyde. We' call Hyde on
it? No, probably because it's with that control. So what we do instead, Just because it doesn't
know what child is, we'll say Dot visible,
equals false. Stuff if we try that we
should see our lives. We see the first one here on the far right disappear once
we get hit with an asteroid. Boom, there we go. And it looks like we somehow got
hit with a second one. That right? Everything
seems to be working. I'm not sure what
that was. Maybe one spawned when I looked away. But there we go. We
have this working, of course, this is
gonna air out now. Or at least it should,
but it doesn't. A little curious
should be telling us that this isn't working
because of the index. That's kind of curious. I
guess we're getting negative, maybe it's coming back around. Anyway, coming through here. So we have our
lives disappearing. We have our lives updating. And based on that, we
would need a lives to go into a game over or not. But first, I do want to
set a minimum number here or maximum number just so that we can't go below
zero in our lives. So what I'm going to
do is I'm actually going to say lives equal to. And we want the max number here between lives minus one and
the second argument is zero. Even though we
shouldn't need this, I'm just going to put
this in just in case to ensure that we don't
have some kind of bug that happens there. So our lives should
never go below zero. And there we have it.
We have that issuing, and all we would do now is
we could call game over if we hit zero and
restart the scene. All right. Now we'll do it here
because all we want to do is update the
lives. We've done that. And next time, we'll
go ahead and set up the game over and restart.
12. Game Over and Restart: Before I game over, I'm going to go ahead
and go into my Guy. I'm going to add another note here. I'm going to add a label. This will be my game over label. This text, it's just
going to say game over. I'm going to put this anchor
it, right here at the top. I'm going to put it
in the sender of the screen to say game
over whenever we lose. We can go into our code and with our update lives,
create a new function. Game over and we'll simply do a check after
we've updated the screen. I will say, I lives equals zero, then we call our
game over function. And the game over function Well, I don't need that to be showing. I'll put that pass back in. I'm going to go ahead and
create an export here for my game label, GameOver label. I'll assign that and
inspector here real quick. GameOver label. So what do. GameOver label dot Vsb equals crew We can pause
the tree, right? So it's a G tree. And actually, I'm not gonna
pause the tree this time. I think we want to get
the player from the Goey. I think the player can
handle the second. Right. How do I want to do this? Alright, so I'm going to click on my player here in my main. I'm going to left click it,
and I'm going to drag it in. And I'm going to release, and this should give me
the path to my player. So I'm just going to say dot
Qu free to delete my player. Once you've run out of Lives
and go to a game over, I'm going to say, I'll wait. Get Tree, create timer. This way, we can go ahead and wait 3 seconds or something. You can go ahead and play with that timer and see
what you like. Dot TO, right? We've
done this before. And then we're just
gonna say Get dot. Reload Current scene. So let's take a look
when we get a game over. To do. We can go
in. We can play. We can shoot, get points. But, but, bah. It's all fine. Boom, we got hit,
we lost one life. Boom, we lost two lives. Was our little ship. There is, and we're going to
lose our last life. Boom, game over. Wait 3 seconds. Game reloads and we
can keep playing. Alright. So there we have it. We've got our game
over simply set. And we're all set to just start playing with
our game here. Now, if you want this
to be a little more authentic to have
the black screen, you can either go into your
project settings and I just search the word color and go in a rendering
environment. Default color. You can
come in here and you can change this color if you want. This is the default
color of the background. You can do that if you wish. But since I usually prefer keeping the default color here, what you can do instead
is you could just come in and do a color wreck, set your anchors to be full. Well, it looks like
it's not going to because we don't have
control as the main. I see. Alright. So we'll just have to go in and
set this up ourselves. So you can just set the size to be whatever size you want. As long as it's big enough to cover the blue area,
you should be fine. And come in and
change it to black. And since we're only going to show what's in the blue area, it doesn't matter
that it's bigger. So we'll go ahead and play Oh, and I need to change
the ordering. So I need to grab
the color retire and actually pull
it up all the way up that way my player
is on top of it so we can see. And there you go. We have something
that looks even more closer to the original by
having the black background. But again, that's
just a personal choice on if you
want to have that. B, boom, but, p, p, p. And it actually
looks like a Our game. Oh, and if you're just wondering why the scoring
that isn't working. It's probably just because
I pulled Color Rect up to be the first child. So Index zero, I'm
trying to connect my signals to color rect
instead of the GUI. So if I just pull
it down so that the GUI Come on,
get out of there. There we go. So that the Guy is first, then that should
fix our issue here. See, boom, Lives now
connecting and our score. So that's just an ordering
layer in the scene hierarchy. So that's just something to
keep in mind if you were to, of course, do it this way and not do your
project settings. Alright, there you go.
We have our points. We have lives. We have GameOver. We have to restart. And we've made once again, another full game without
needing any resources. We've made everything
from within the engine. All right, that'll do it. We now have asteroids. And if you want to take
it a step further, you can go ahead and again, you can find sprites online, or you can use the Line two
D and go ahead and create those spaceships that you've seen and assign
them some points. And the same thing,
give them a signal, connect it up to your GUI, send the points with it, just like we do with the asteroids.