Godot 4.5 Beginner Tutorial: Auto-Aim Turrets & Nearest Enemy Logic (Part 3)
566 segments
Now we have a horde of monsters chasing
us and our only defense well is running
away. It's time to fight back. Today we
build the auto aiming system.
Welcome back. In the last video we
created the horde. Today we create the
cure. We are going to build a weapon
that scans the environment. Does some
quick math to find the closest threat.
It automatically takes the shot.
First we need something to shoot. So,
we're going to use an area 2D because we
don't need it to bounce off the walls.
We just need to detect when it touches
the enemy. So, back in our game here,
what we're going to do is new scene
scene. New scene.
Go back to our 2D so we see what we're
looking at. And we need an other node.
We're going to use the area 2D
there. And rename this to projectile.
There we go. And right click there, add
a child node, sprite 2D.
Now, for this one, I just use a texture.
So, I click on the texture node here.
I'm just going to use a gradient texture
2D and just make a little ball here. And
how we do that, we can click in this
texture node here. And then in the fill
here, the fill section, linear, we want
radial. Perfect. Or make it a little
smaller here. We'll do
uh we'll do 8 by 8. Perfect. Then what
we're going to do here is the from
section the X 0.5,
the Y 0.5,
and the two 0.5.
There we go. And then in our gradient
section here, we want to click
interpolation,
then mode. Instead of linear, we want
constant.
There we go. Now, we have this little
pixel kind of ball thing here, but it's
not transparent on the edges. So, how do
we do that? Well, we're going to click
in this little bar here, and then click
in this little square here. We're going
to make this white to start.
So, if you want to add some color tint
to it later, it's easier. So, now that's
white. And click out of there. And then
to make the edges transparent, click on
this last bar on the right. And then in
this square here, change the alpha all
the way down to zero. Perfect. Now click
out of that. Now we have this little
circle pixel circle. And that'll do for
now. We also need something to tell it
collision. As this node configuration
says, it needs a collision shape. So
right click, add collision shape 2D.
Double click there. Add that. We're just
going to use in the shape another
circle. We zoom in here. Zoom in here.
We just want our collision shape to
match
the pixel. Maybe make it a little wider.
Perfect. Now, in our projectile section
here, click on the root node. We want to
go into our collision. Now, we don't
want to collide with our player. We
uncheck that. We also want to uncheck
the mask. We don't want to detect
collisions with our player. We only want
to check the mask or collisions on the
enemy. Now, you don't need it be on a
layer specifically. We're just going to
check the mask if it collides with the
player. And then it will do whatever it
needs to do. So then we need to attach a
script to say what we want to do when it
collides. With the projectile selected,
add script. want to add not just in the
root node, our root path, but I go into
our scripts folder. Open. And now it's
in our scripts folder. Create.
Now I'll make this bigger. Get rid of
this output. Click in there. And we get
rid of this boilerplate code for now. We
do not need that.
All right. First thing we need, we need
to see how fast it's going to go. So
we'll make a variable. Speed is going to
be uh 600.
And then we need where we want the
projectile to go. So we need a direction
and we're going to set it to default to
vector to do right.
And then we'll change that depending on
where it needs to be going. And next we
want to have a variable see how far it
went so we can keep track of where our
bullet is. So once again if it's it's
gone too far range range traveled just
like our enemy we want to get rid of it.
We don't want them flying around
forever. We'll set this to zero. Then we
do need a function, the physics process
function, the delta and float.
And then we want to enter down into the
function. The first thing we want to do
is move in the set direction that we
find. And how that well, we'll store the
movement in a variable. And then we'll
use the direction that we set up top and
multiply that by the speed.
And for this one, because we're not
using the move and slide, we do need to
use our delta because this will be
frame. We want to make this frame
independent because we don't want to
have a higher graphics card means your
bullet will just fly a million miles an
hour. So, we're going to limit it to how
we want how fast we want it to go, which
is a 600. And multiplying by delta will
do that for us. Those familiar with the
Unity engine, it's timed delta time,
those kind of things. Same thing. And
after that, well, we have the movement.
Now we just need to move the position of
the whatever this is attached to. And
we're going to plus equals that to the
movement.
Great. Now we got our projectile moving.
Now we just need to see if we need to
delete it or not. If it's gone too far.
So the range check if it delete. Delete
if it goes too far. There we go.
Perfect. Now we do that. We just take
our range traveled we set up there. And
then we're gonna add the length of the
traveled projectile.
All right? So the length, so how far
projectile has gone. Then we're going to
check if that range has traveled a
certain distance.
So greater than say a,000 pixels. Well,
we're going to get rid of it. There we
go. And not redraw. Not redraw. Free.
There we go. Want to delete. Not come
into existence again. All right. Now
that we've deleted that, we need to
actually
save and we need to save it in the
scenes folder. Oh, this is saving the
scene. That's fine, too. We'll save the
scene. Perfect. Players projectile
script is saved. Let's minimize this
real quick. Now, we need to go into the
projectile
root node and then in the by the node by
the inspector, we click the node here.
Oh, if it does this, we can click away
and click back on. There we go. Now,
what we need is the body entered. So, we
need to connect this to our projectile
script. And this will tell us when
something has entered the body of the
projectile. So, we can double click
that. Make sure it's on the projectile
script and connect. Then, I'll maximize
this script again.
Then, now we have the on body entered.
Now, what do we want to have happen when
something enters the body of the
projectile? Well, since we only I
minimize that again. Go into the
inspector. Since we only put the mask
two,
anything that this detects is going to
be only an enemy. Now, if you had a
player on here as well, you would have
to differentiate between, well, is this
a player or is this an enemy that this
projectile has collided with? But since
we only set two of the enemy,
we know this projectile has hit an enemy
if this gets triggered at all. That
makes coding a little simpler here. So,
I'll maximize this again.
So, okay, delete this
boiler plate here. So, I'll make a note
of that. So we only mask layer 2 enemies
or enemy.
So the body that is being passed in here
must be an enemy. So now we know. So all
we have to do is check
if body
has method. Now we're going to write
this method in a second. So it doesn't
exist yet, but it will. We're going to
put a method called take damage on our
enemy.
So there we go. So if this body has the
method take damage, so we want the enemy
to take damage. For some reason, we can
make an invincible enemy and we can not
have it have that. Well, then our
projectiles won't hurt it. We want to
call body.
Damage only if it has this function.
Now, this doesn't know if it does or
not. So this if is statement is going to
save us from throwing an error if it
doesn't have the take damage function.
So else
after it's take it's checked that we
just want to delete it for now. Q3.
So instead of letting him live forever
or projectile not do anything. Well,
we're just going to delete him anyway
for now until we want to get more
complicated with this. So let's make a
note of that. So we'll add
this method
to enemies
to enemies
spell here in video five. Okay, the next
one for now let's just
delete them so we can do other things.
Perfect. After we delete the enemy,
after we hit it with a projectile, we'll
deal with health and all that in a
little bit. We're just going to delete
the bullet. Q
three.
Don't be so hard to spell. There we go.
So, what does this do? This just
destroys the bullet. So, the bullet gets
destroyed if it hits something or gets
uh this projectile.
Destroys the projectile. So, the
projectile gets destroyed if it hits an
enemy and the enemy gets destroyed if it
gets hit by a projectile. Um, just in
this logic, this is expandable. So, it's
nice to have in case you want something
else to happen when the bullet hits
them. And when we get a take damage and
a health bar for the enemy, they won't
die in the first hit. And since we know
body is an enemy, we don't have to check
if it is or not. Great. Now, save that.
S or file save scene save.
And now, let's equip the player. We need
a scanner to tell us who's nearby. We're
going to use an area 2D again, but not
for physics, just for detection. I go
back into our player scene here. I'm
going minimize this. Go to my 2D. Now
I'm going to
make those smaller. And in my root node
of my player, I'm going to add a child
node 2D.
I'm going to rename this to holster.
I'm going to add a timer to my holster
here. And then I'm going to set the wait
time on this timer in the inspector to
0.3.
So that'll fire pretty fast right now.
So we'll check auto start to on,
but we also need to have an area 2D
selected. Select our holster, right
click, add child node, area 2D.
We want to rename this to the
rangefinder.
And to the rangefinder, we want to add a
child node, collision
shape 2D.
And this collision shape, we're going to
make it a circle shape 2D in the
inspector. And we're going to make it
really big. So I found 320 for the
radius. And you see this 320 highlights
all up and down entirely of our screen.
I don't want it shooting outside of our
viewport here. So I want to be able to
shoot the enemy if I can see them. And
then this safe spot here in the corners
for the enemies. So, we won't shoot them
in the corners. We're only going to
shoot them when they enter our collision
shape 2D. Now, for our collision in our
rangefinder here,
rangefinder selected. Go into collision.
Now, we don't want to worry about the
player. We don't want to be on a layer
there. We only want to find the enemies.
So, once again, deselect the mask for
the player and just select the mask for
the enemy. We only want to detect the
bad guys.
I reached a checkpoint. It's the nearest
neighbor problem. Now, the rangefinder
can give us a list of every enemy inside
the circle using get overlapping bodies.
But the list that it give us is
unsorted. It might give us an enemy 500
pixels away, ignoring the one right at
our ankles. So, we need to write an
algorithm to sort them out. So, we're
going to loop through the list, measure
the distance to each one, and then save
the winner.
So, let's script the holster. Now, we're
going to connect the timer to fire, but
only if we find a target. So, to the
holster here, we're going to add a
script
not in our scenes folder. Go to our
path. Go up to our root. Go into our
scripts. Holster.gd is fine. Open.
Create.
And let's get rid of this boilerplate
code for now. Now, first things first,
we want to take our timer. Click there.
And we want to go into our node section
right by the inspector and connect this
timeout function to the holster, not the
player. So scroll down to the holster.
We want to connect this timer to its
holster. So now that we have that,
connect. That should pop up in your
holster script that we just wrote. Now
let's get a couple variables here up
top. The first export one. Export var.
We need the scene of the projectile we
want to launch from the holster. So the
scene and it's going to be a packed
scene. I'll go ahead and make this
bigger. There we go. And then an on
ready. On ready will fire when the node
is ready. Is going to be the
rangefinder.
Okay. And that's going to equal to our
money symbol rangefinder. Once again, it
should pop up. If it doesn't, perhaps
you changed your node name to something
else or you didn't rename it at all. So
in our minimize this. So in our holster
section, we change this area 2D to a
rangefinder and that is what we're using
in this already. We'll make that bigger.
Now that we have those, we need to go
into our timer timeout function
and make that work. So the first thing
we want to do is we're going to get all
the enemies in range. So we have that in
a big list. So var enemies in range
in range. That's going to be the
rangefinder. So we have that. We do that
one we called get overlapping bodies.
That's what we want. And then after we
have all the bodies in range. Well,
first we need to check if there are any
enemies. If there are no enemies, we're
going to stop. Stop here. How do we do
that? Well, if the enem is in range is
uh size is zero. Well, return stop.
Don't need to go any further. But if
there are enemies in range, well, we
need to find the closest one.
So, first we're going to set our target.
We're going to keep it null
right there. So if there isn't a target
enemy from the last time this ran after
the timer timed out before, we want to
reset that to null. We want a new enemy.
So we also want to get the shortest
distance.
So we make a new variable shortest
distance
equals
NF.
Now what is this? Well, that's infinite.
So first we're going to start with
infinite distance and then we're going
to narrow it down from there. infinite
distance. Perfect.
And then we're going to loop through our
enemies to find out which one's closest
to us. So for the enemy with enemies in
range, well, we need to measure the
distance from the holster. Measure
distance from holster to enemy.
So after we do that by well we store the
distance in the variable and then we
want to get the global position.
Then once again we use the distance to
just like we did with the enemies
but we want to check the distance to not
the player but the enemy enemy
global
position.
There we go. There's that.
Then we have that we're going to check
the distance now that we have the
distance is less than the shortest
distance.
So it's all the distances save the
shortest distance from our infinite and
as it loops through it'll grab that
shortest one and once it has it well the
shortest distance becomes the distance
or the distance becomes the shortest
distance. Sorry, I had that backwards.
And then our target enemy
becomes this enemy we got the distance
to here, the target enemy is now that
enemy.
There we go. And last but not least,
well, after we have the closest enemy,
well, we need to fire. Fire at the enemy
or the winner. Yeah, fire at the winner
to the shortest distance. Perfect. So,
if the target enemy that we set in the
loop. Well, we're going to shoot target
enemy.
Great. And notice we don't have a shoot
yet. Well, we're going to do that. Now,
we found the target. Now, we need to
create the projectile. Remember, we must
not add the projectile as a child of the
holster or it's going to move when we
move. It needs to live in the world.
Much like we did with the enemy with our
mob spawner, we use the get tree to
spawn it on the root node of the current
scene. We do much the same here. So in
our holster script here, we're going to
do create the new function called shoot.
Enter down a couple times and then
function funk shoot. And what are we
going to shoot? Well, we're going to
shoot the target that gets passed in.
The first thing we need to do is create
the projectile. So, new projectile
is going to equal the projectile
scene that we have up top. And we're
going to instantiate it.
And now that we have that, well, we need
to set the starting position
to the holsters
position. Perfect. So, we're going to
make a the new projectile.
New projectile. There we go. is going to
be the global position
of the holster which is the global
position which just accesses what this
script is attached to. So then we need
to calculate
the direction to the target to target.
Perfect. Then the var direction
2 target our new variable. Well, that's
going to equal to the global position
dot direction two. Again, really handy
function there. Well, we need a
direction to what? We need a direction
to the target.
Global position. Ah, I don't spell
right. Global position.
There we go.
Now that we have that, we have the
direction to the target. Well, what we
need to do, we need to change the
projectiles direction. New projectile.
We need to set the direction
to the direction that we just got. So,
direction to target. All right. Now, we
have all the calculations done. Well, we
need to look at it. So, say you had an
arrow or something like that. instead of
a ball, we want to rotate the projectile
to actually face the target it's going
to attack. So what we do is rotate
projectile visually
to face target. And we do that just by
saying the new projectile
look at
to look at the what we want to look at
the target. Global position.
Global position.
Perfect.
And last but not least, well, we need to
add it to the main world, not the
holster. So, how do we do that? Well, we
get the tree of the scene. So, get tree
of the current scene we're in. So, it'll
get the root node. We want to add the
root node. We want to add the child to
the root node. So, we want to add the
child. The child we want to add is the
new projectile.
Perfect. Now, after all that, Crl Save
all of that. I'm going to make that
smaller. Now, in our holster here, back
in our inspector here, we need to do is
set the projectile we want to fire. So,
this projectile scene is going to be our
projectile scene. Click and drag that
and drop it in there. Now, it knows
which projectile to use.
So, after all that's set up, we're going
to go back to our we're going to save
that. Go back to our main game here. And
then we're going to run the current
scene and give this a test. So, running
the current scene, we're going to run
around. Soon as the enemies start
popping up, there we go. See our bullets
start firing. They fly off. They're
going to delete themselves. Let's make
sure they do. If we go into our remote
section here, go to our main game, we
can see our
projectiles,
our enemies as well. Here and our Let's
open up our game here. There we go. We
also see our projectiles. So, if we run
around and then our projectiles start
missing, well, they don't keep stacking
up and the enemies don't keep stacking
up as well. This will keep all the stuff
optimized. because you don't want too
much of this stuff on the screen. Even
with 2D, it will bog down anyone's
system eventually.
And there you have it, an automated
turret system. We scan the area, loop
through the array to find the closest
threat, and instantiate a projectile.
But right now, killing enemies gives us
nothing. So, in the next video, we're
going to add the dopamine. will
implement the XP system, enemies
dropping gems, a smooth XP bar, and the
level up screen. Now, if you found this
helpful at all or fun, leave a comment
what your GDO game dev goals are. I love
seeing your stuff. Now, please like,
subscribe, and hit that notification
bell. It really helps out the channel.
And thank you to all our current and
past Patreon and coffee members. Your
generous support keeps the channel
moving. Now, if you want early access to
tutorials, source code, or suggesting
future tutorials, please consider
becoming a member yourself. The links
are in the description. I'm Spaghetti
Syntax, and remember, finished not
perfect, fail fast, fail often, always
stay curious, and I'll see you at the
next checkpoint.
Ask follow-up questions or revisit key timestamps.
This video explains how to create an auto-aiming weapon system in a 2D game. It covers the creation of a projectile, its movement, and collision detection. It also details setting up a rangefinder for the player to detect enemies and a timer to trigger shooting. The script for the player's 'holster' finds the closest enemy using `get_overlapping_bodies` and then instantiates and directs a projectile towards it. The video concludes with a demonstration of the auto-aiming system in action and previews the next video's focus on adding an XP and reward system.
Videos recently processed by our community