Discover how to aim ranged projectiles that hit a target despite having gravity applied to it.
You can use this for creating enemies that throw projectiles at your player, or for allowing your player to precisely aim their own ranged attacks.
Howdy, and welcome to Game Endeavor.
Learn practical dev skills and how to polish your games by subscribing and ding-a-linging that bell.
Gravity is a neat trick performed by our reality,but when implemented in our games, it has a tendency to complicate a few things.
If you want gravity to affect your projectiles,then it will be a little tricky to aim them precisely.But with some code, your attention, and a bit of magic, we can solve this issue.
Let’s go ahead and get started.The code that we’re going to be writing has many uses and you may want to use it more than once in your game, so we’re going to write this code inside of a helper script that we can call anywhere within our game.
However we’re not going to auto-load it,instead we’re going to be creating static functions in this script which can be called without being instantiated.
So I have created a script named Physics Helper. Gd where we’ll write our code. We do however need to be able to reference this script, so it has the `class_name Physics Helper`. We’re going to create a static function named `calculate_arc_velocity()` and it’s going to need a few parameters.
`point_a` which is going to be the position of the projectile before it is launches. point_b` which will be the position where you want it to hit.
`arc_height` which is how high above `point_a`that the projectile will be thrown, and `gravity` which is that mystical force that forces that lobs apples at our head.
I have mine generated in an Auto Load script named Global, so I will default mine to `Global.
Gravity` In order to pull off this trick shot, we need to know a couple of things.
We’re going to be using some equations of motion.
I wont get into the nitty gritty of the equations in this video, though I may do a separate video on them in the future.
For this video, we’re just going to buckle in and force our way through this in a timely manner so that we can get to the fun stuff like how to apply it. Before we get started, let’s create a variable named velocity, so that we can store our result. We’re going to instantiate it as an emptyVector2.
First we’ll calculate the vertical velocity,which is equal to the negative square root of `-2 * gravity * arc_height`.
If this seems familiar, it’s because we did something very similar in the video where we set our jump height and duration. In fact, it would be exactly the same had I done it properly. But some of the signs are changed around,likely to allow us to set a positive value for jump velocity.
Now we want to find our horizontal velocity,but to do that will be a little trickier.We’re going to have to find out how long it will take this projectile to reach its target.
Luckily we can get this by using some more of those fancy equations.
Before we do, let’s get the displacement by subtracting `point_a` from `point_b` and storing it in a variable. This is the x and y difference between the two points.
`time_up` can be found by taking the square root of `-2 * arc_height / gravity` ironically casting gravity as a float to prevent rounding errors. Time down is ever so slightly different, but close enough to be confusing.
It is the square root of `2 * (displacement.Y- arc_height) / gravity` again casting gravity as a float.
Having gazed into the dark depths of time,we know everything we need to get our horizontal velocity, which is equal to `displacement.X/ (time_up + time_down)`, casting our time as a float.
This function assumes that gravity is consistent both up and down. Which is realistic, but as a game developer,you don’t have to be realistic.
In fact, I highly recommend bending the laws of physics to your will to improve the polish of your game.
As my friend Decroded mentioned in our Discord:faster down gravity is really good for fighting games where the player may not want to spend much time floating in the air.
Personally I prefer a slower down gravity because it makes it easier to land tricky platforming, but different games will have different reasons to manipulate gravity.
We can fix our function by having two seperate gravity variables in our method.
First we’ll rename the current gravity variable to up_gravity.
And then we’ll add down_gravity as an argument,defaulting it to null.We’re defaulting it to null instead of Global. Gravity,because when calling this method, you may only want to use one gravity.
If you were to set the up_gravity to a custom value and not set down_gravity, then down_gravity would remain the default value. Doing it this way, it will instead be null. Wait, what? Did I. Well that’s not much better… I suppose we could check to see down_gravity is null, and if true then set it to up_gravity.
And then we need to change the gravity variable when setting time_down to use the down gravity instead.
Now you can assign different gravity values while using this method.
There are a couple of things to keep in mind when using this.
The arc height needs to be higher than they displacement of point_a and point_b.That’s because there would be no down time since the projectile wouldn’t have enough velocity to reach its target position.
You could check to see if `time_down` is nota number.If true then set it to 0.This will make it so that the apex of the arc is directly under the target.
Alternatively you could check to see if they displacement is less than the arc_height before you calculate the velocity.This will keep velocity at an empty Vector2.
Which you could check for to determine if you should cancel what you needed the velocity for.
Though if you keep watching, then we’llfix it so that we don’t have to worry about this situation.
Another thing to keep in mind is that `arc_height`has to be less than or equal to zero, because even if time_up were to be 0, the apex of the arc would start directly at point_a, which would be an arc_height of zero.
It would be impossible for it to start under point_a.
These rules are fairly restrictive, but it’s really easy to work around.
Rather than setting the arc_height to a specific value relative to point_a.
We could instead set arc_height relative to point_b, so that it goes a certain distance over head.If when spetting the velocity, we set arc_height equal to `target_position. Y – global_position.Y` of the projectile, then subtract the amount we want to be over the target.
This will ensure that the projectile is always above point_b.
However, it is then possible that the arc_height could be under point_a if point_b is low enough, which would be an error.
To fix this, we have to use the min function to make sure that arc_height doesn’t go greater than 0. I will use -32 as the 2nd argument. With this, the projectile will always be thrown at least 32 pixels over its origin.
You will notice however that if point_b is really far away from point_a, then the velocity will be extremely high. We can fix this by clamping the velocity toa certain length.
It means that it won’t hit its target if it’s too far away, but it will limit it to a certain speed. We can add a bit of inaccuracy by making some adjustments. If you add a slight randomness to the rotation of velocity after you calculate it, then you’ll lower the accuracy. This is an interesting method, because the greater the distance, the more the accuracy is affected.
But close up, it can still be very accurate.
Another option is to add a random offset to the target position before calculating the velocity.
This will make the accuracy consistent regard less of range, but that also means that it can be inaccurate even at close range and is a lot more noticeable to your player.
Watch this video to learn more practical dev skills that will improve the quality of your games. And if you’re new, then join the sub-club to get notified for future videos.