How to Create Ranged Attacks in Godot 3.1


In this video, you will learn how to create
projectile attacks that you can use to damage distant enemies or other entities in your
game such as barrels or signs. We’ll cover how to spawn in your projectile,
various ways that you can handle its velocity, as well as how to handle it’s collision.how
to handle its collision Howdy! And welcome to Game Endeavor, where you can
learn practical dev skills from an experience developer. If you want to allow your player to attack
enemies from a safe distance, then you will need to implement a ranged attack system in
your game. There are countless ways to handle projectiles,
but if you don’t plan it properly then later on you may find yourself restricted by your
own code. For this tutorial, we’re going to be taking
a modular approach that will give us a lot of flexibility to expand upon in the future. Let’s go ahead and get started. The first thing we’re going to do is go
ahead and create our projectile scene. We will instantiate this scene every time
we want to attack with a projectile. I have already prepared a scene with a KinematicBody2D
as the root, a sprite, and a collision shape that will be used to detect when the projectile
collides with an obstacle. I’m using a KinematicBody2D for reasons
that I’ll get into later, but if you want to be more efficient with your projectiles
then you can use an Area2D as an alternative. The root has a script, which we’ll step
into to write the code for our projectile. We’ll go ahead and create a variable named
`velocity` which we’ll set as `Vector2.ZERO`. This will determine the distance per second
that the projectile will move. We’re also going to need a velocity to set
this to when we launch our projectile, so I’ll go ahead and create a constant named
`THROW_VELOCITY` and since this particular projectile will be thrown up into the air,
I will set it to a Vector2 that is up and to the right. If you want to throw at a specific angle like
in a top-down game, then you can just use a float constant instead which you will later
multiple be the normal of the direction that the projectile will launch in. In the next tutorial, I will show you how
you can aim this velocity so that it will hit a specific position with gravity being
applied. This can be useful for allowing the player
to aim their shots, or giving enemies better aim when attacking the player. Now we need to put our velocity variable to
use, which we’re going to do inside of the `_physics_process()` method. I am storing my gravity variable inside an
AutoLoaded script named Globals, so that I can call it anywhere in my script. Which I’ll do now by multiplying `Globals.gravity`
by `delta` and adding it to the y value of my velocity. Applying gravity is optional and your projectile
will have a completely different feel without it. Applying gravity is optional, and your projectile
will have a completely different feel without it. Now we need to apply the velocity to our projectile. I will be using move_and_collide for this,
because it returns collision data which I can put to good use. Unlike `move_and_slide` however, `move_and_collide`
does not apply the delta to velocity automatically, so make sure you do this when you pass it
to `move_and_collide`. We’re going to store the return value in
a variable named `collision`. With this collision data, we can know whether
or not we hit something. We check this by seeing if collision is not
null. How you handle your collision will be completely
dependant on the projectile and your game. You can either destroy the projectile, make
it stick into the wall if it’s an arrow, or what I’ll be doing which is causing it
to bounce off of the surface. The nifty thing about using this method to
check for collisions, is that one-way collisions is already handled for us. If you were to use an area, then it would
detect the collision despite it being one-way, and you would need to do some fancy logic
to determine if you should ignore the collision. We’ll handle the bouncing logic inside of
a method named `_on_impact()` which we’ll create. This will require a normalized Vector2 as
an argument, which we’ll name normal. I want my rock to bounce off of the surface,
so I’m going to set the velocity of our projectile equal to our velocity bounced against
the normal. Now as is, this will give the rock infinite
bounce. Which would be great if you’re thinking
with portals, but I would like for my projectile to eventually come to a stop. We can do this by lowering the velocity every
time it bounces. Which I’ll do by multiplying velocity by
0.5. The higher this value, the more bouncy the
object will be. For a little variation, you can also add some
randomness to the bounce by adding `rand_range(-0.05, 0.05)`. With the basic physics for our projectile
implemented, we now want to create the logic for launching it. How you handle this will be dependant on your
game. For this tutorial, I will be spawning the
rock in the player’s hand, Paw Bearer will then lunge forward with the rock, he’ll
release it, and then follow through returning to the idle position. We want the rock to release partially through
the animation rather than immediately. Which means that we don’t want to apply
our physics logic immediately when it’s spawned. So we’ll create a ready method and in it
we’ll disable physics processing by saying `set_physics_process(false)`. We’ll enable this again when we’re ready
to apply the physics. Now we’ll create the method that will apply
the throwing logic. This method is named launch and will be called
outside of this class by the entity that is launching the projectile. We’ll need to know what direction to throw
the projectile in which will be the direction parameter. For me, I just need to know the direction
of the x-axis so it will be a sign value that will determine if the projectile is moving
left or right. If you’re using the y-axis such as in a
top-down game, then you will need a normalized Vector2. When we launch this projectile, it is going
to start parented to a position node on the character’s hand. We need to reparent it so that its position
is no longer relative to the character’s hand. Otherwise you will end up walking the dog. To do this, we’re going to tell its parent
to remove itself by calling `get_parent().remove_child(self)`. Now we need to re-add the projectile to the
scene. The easiest way to do this is to add it to
the current scene, which we can get from the SceneTree. However you need to be in the SceneTree to
properly call the `get_tree()` method, which you’ll recall we just removed ourselves
from. To get around this, we’ll get the current
scene before we remove ourselves and store it inside of a variable named `scene`. With that done, we can now tell `scene` to
add ourselves as a child. However, the projectile doesn’t get to keep
its previous location. It gets reset and will spawn at (0,0). We will go ahead and fix this before it can
cause of any headaches. Luckily, all of the data we’d want to carry
over is stored inside of a transform variable. We can get our `global_transform` and store
it inside of a temporary variable before we reparent, and then after we’re back in the
tree, we can apply our temporary variable to our global transform. If you’re launching your projectile immediately
after spawning it, then you can skip all of this reparenting process and just set its
global position to where you want it to spawn. Now that our rock is independent of our character’s
arm, we can start applying velocity to it. We can do this by setting `velocity=THROW_VELOCITY
* Vector2(direction, 1)`. The 2nd Vector2 is meant to modify the x value
of `THROW_VELOCITY` so that it moves in the correct direction. If you’re using a normalized Vector2 for
direction, then you would instead multiply the float value mentioned earlier by your
normal. And since we’re now officially launched,
we’ll also want physics to kick back in, so we’ll enable physics processing again. We can now give this rock to our player. I’m using the cutout character rig that
I covered in a previous tutorial. This makes it easy for me to parent a position
node to my character’s hand where I want the rock to spawn. I will also set the position node to render
behind its parent, that being the arm. And temporarily I am going to instance a rock
to the position node so that I can set the rotation of the position node so that the
rock is rotated how I want it when released, but I’m going to delete the rock afterwards,
otherwise the player will always be holding a rock. Inside of my character rig script I have added
a signal named `throw_item` which we’ll use to tell the player when to release the
item. I have premade a throwing animation. I want to emit that signal from inside of
the AnimationPlayer, which I can do by creating a Call Method Track and inserting a new key
on the frame that I would like to emit the signal. And we can use this key to call the `emit_signal()`
method, modifying it to accept our signal name as the parameter. I can then go into the player scene, connect
the `throw_item` signal to my player. For convenience I will have it call a method
“throw_held_item” directly. At the top of my player script, I want to
cache the reference to the PackedScene of the projectile inside of a constant, which
I will name RockProjectile_PS. I also want to cache the position node that
I created earlier, so I’ll store it inside of a variable named held_item_position. Since I’m not throwing the held_item immediately
after it spawns, I will need to store a reference to it, so I’ll create a variable named `held_item`
within my class variables. Before we can throw the rock, we first need
to have a rock. So I’ll create a method named `spawn_rock()`. We don’t want to spawn a rock if we currently
have an item in our hand, so we’ll check to make sure that `held_item==null` before
continuing. Then we’ll instantiate the rock by calling
`RockProjectile_PS.instance()` and store it inside of our `held_item` variable. And finally we’ll add the held_item as a
child to our held_item_position node. We’re ready to throw the held item now,
so inside of the `throw_held_item()` method that we created with our CharacterRig, we’ll
call the launch method of our `held_item` reference, passing to it our facing variable,
which is just a variable that we have set to what direction our character is facing. And then we’ll set held_item to null since
we no longer need to reference it, and would like to know that our furry little paws are
empty. Now that you’re able to launch projectiles,
odds are you would like for them to detect entities and damage them. I use two custom scenes for this. The first being a hitbox which is just an
Area2D set to its own layer, but it has a simple script on it with an export variable
that will allow you to set the node that will be damaged. It exports a NodePath that defaults to “..” which
refers to its parent, and then it has an onready variable that uses that NodePath to get the
actual node for it. Just instance this node onto your character,
give it a shape, and try not to giggle when caching it into a variable. Then I have a DamageArea scene that is also
an Area2D but it masks for the hitbox layer. And inside of the script, it will keep an
array of entities that it will not interact with, and its `area_entered` signal is connected
to itself and once it detects a hitbox, it will make a few checks and then call a damage
method on the hitbox’s entity. You could take this a step further and add
group exceptions so that you don’t get friendly fire. I will go more into further detail on this
system in a separate tutorial about health, damage, and all that fun stuff. Add the DamageArea to the projectile and then
give it a shape. To actually throw the rock, you just need
to spawn the projectile and then play the throw animation which will handle launching
the item. You may want to do this inside of a state
machine. I have videos on state machines that will
help you with that, there’s a playlist in the description and an icard in the corner. If you want to learn more practical dev skills
then watch this video here. And if you’re new then join the sub-club
to get notified of future videos.

13 thoughts on “How to Create Ranged Attacks in Godot 3.1

  1. Howdy! I was hoping this topic would be a lot more simple than it turned out to be, but I can't help myself from going above and beyond to make sure I give as much information as I can in a timely manner. It was a lot of fun to make though. Enjoy the video and let me know what you think. Thank you for watching!

  2. shouldn't you be using rigid bodies instead, for better performance in case you decide to have too many projectiles at the same time?

  3. Thanks for the vid. I will probably need to watch this multiple times to understand the intricacies of what you are doing.

  4. The fundamentals of this video are great. Thanks!! People who come here to learn will get exactly what they want. Do Not worry about making things perfect or optimized while learning. Understand the code and use it however you need. Polish comes later.

  5. Hi, i was wondering what the needed setup would be for having the projectile stick in the wall as in the video. Thanks in advance for anytime and effort.

Leave a Reply

Your email address will not be published. Required fields are marked *