Simple projectile calculations in 2D with your own movement code

So last time we did a simple projectile calculation by using Unity’s rigid bodies by calculating velocity vector and applying a force to the projectile rigid body. Now we do the same without rigid body but doing a calculation based on updating the position manually.

Remember this was the sketch of what we have:

projectile_2d_001

For updating the position we need to know the angle, distance, amount of gravity and time of flight. We also need to calculate the velocity which we need to do other calculations. The distance as you remember from the last time we get by: d = sqrt(pow((x2 – x1)) + pow(y2 – y1)), or Vector3.Distance(start, end) or (start,end).magnitude (where start and end are Vector3 objects).

So in our example: d = 15.0, a = 66.0, g = 9.81, replacing the variables in the equation give us v = 14.0716. Now we are interested how long the projectile will stay in the air. This is because we need to interpolate the position between the start and end positions. We could use some a fixed value for time but this will create unrealistic result since the actual time spent in the air depends on the angle and distance (and gravity).

So how we get the time? You can calculate the time the projectile is airborn in two ways. Either you can use the Y component (eventually gravity pulls the projectile back to ground) or X component. If you want to use Y, the formula is time t = (velocityY * 2) / gravity. So y velocity times 2 over gravity. If you remember from the last time we used the velocity and direction to get the horizontal velocity and sin(angle) * velocity to get Y vertical velocity? We use the same method like this:


var direction = (target.transform.position - start).normalized;
var horizontal = Mathf.Cos(angle * Mathf.Deg2Rad) * velocity;
var vertical = Mathf.Sin(angle * Mathf.Deg2Rad) * velocity;

Finally we can calculate time: t = (vertical * 2) / g. So in our example it is: 2.62s This means that the projectile will stay in the air 2.62 seconds without hitting the target. So bigger the angle more velocity we need to hit the target thus longer the projectile will stay in the air. You could also use t = distance / horizontal, which by the way also gives the same 2.62. (which is a good thing!)

Ok, we have now all the components needed for our calculation. We are using Update() function which Unity will call every frame. This makes the function frame rate dependent, e.g. with 30 fps Update will get called on average by every 33.33 ms. So we know that we should get from start to the end in 2.62s. I said we need to interpolate the position based on the time. This can be done in several ways.

Unity has a Vector3.Lerp -function what is very handy what we can use e.q. Vector3 currentPos = Vector3.Lerp(start, end, time / duration); If the use this helper inside Update() -function we need to increment time variable by the difference of the previous call and Time.deltaTime is exactly for this. This way you make sure your stuff is not frame rate dependent. So a small example:

private float time = 0.0f;

private void Update()
{
time+=Time.deltaTime;
var currenPos = Vector3.Lerp(start, end, time/duration);
}

Duration you need to be calculate somewhere outside Update and start and end are positions as Vector3 type.

If you update your projectile currentPos, it will fly from start to end in given duration, but since it’s a linear interpolation between the values it do not have the projectile trajectory yet. We actually use only the x component to update the x position but we also use it to calculate y position. So how it’s calculated? We go back to the original Wikipedia page and see that height at arbitrary distance x has a formula:

height y = start.y + x * tan(angle) – (gravity * pow(x) / 2 * pow((velocity * cos(angle))))

so when in our Update() function we apply the x from current position to the formula, we have the height at distance x. The update function is going to need something more than that, for example you don’t want to update the position unless the projectile has been launched and you don’t want to update the position after time > duration. But that’s easy stuff after all this.

Neat, now you can hit the target by any angle and distance and you are doing it all by yourself! What do you think? Do you have any suggestions or improvements? Leave comments!

Simple projectile calculations, first in 2D by using rigid bodies

It’s been a very long time since I posted anything programming related here. The reason being I have been busy at work working with two different games and I have felt I didn’t have the energy to do anything extra at home.

But just recently I was working with a new game play feature where I needed to calculate a projectile movement. This was not something I have done before and I had to dig up some stuff to make it work and I wanted to share some of the ways how to do this. It’s basically not rocket science but there are some considerations how you want to do it. This is the first part of the series where I cover some techniques how to do the calculations. First we concentrate on 2D space and later how to do that same in 3D. The reason is it’s easier to understand what’s needed with just 2 axis rather than with 3 axis. I have done the code by using Unity3d but the principles applied are universal.

So in my task I needed to calculate projectile trajectory in 3D space where I know the start and end coordinates, possible throw angle but not the velocity needed to hit the target. In the game I absolutely want the projectile to hit the target so this is directly not applicable to calculation in a game mechanic where you can freely select the angle and possible velocity (thrust / power), and calculate how far the projectile will go. The formula is still the same but you would need to solve different factors from the formula.

So firstly, if you use Unity3d you can choose at least two different ways to do the projectile. 1) Using rigid bodies and applying a force to the rigid body and let Unity take care of the projectile movement. This is the easy way. You don’t need to calculate a whole lot of stuff just apply a certain force and boom, off you go. 2) You do it manually. You calculate the projectile movement by yourself.

Solution 1) is just fine if you can do that. With ridig body you lose the control how the physics simulation takes place (it’s carried out by Unity3d). This might be ok for certain game play mechanics, but was not for me. For example I needed to be absolutely sure that the projectile will end up in the target. Also if you want to have automatic collision detection with colliders you might have a situation where you have something in front of the actual target and colliding that instead of intended target. This is of course not realistic but if you are reading this you are probably not making a realistic FPS anyway.

Solution 2) is where you do calculations manually. This gives you absolute control of the position of the projectile and still allows you to do collision detection with colliders.

Ok, let’s draw some stuff. This is your game where you have source (red diamond) and target (green cube) in 2D. Source position is x1, y1 and target position is x2, y2. The angle between x -axis and projectile is theta which is 66 degrees in the example.

projectile_2d_001

With solution 1) you need to calculate the velocity vector which is applied to the rigid body of the projectile. You have two components X and Y that you need to solve because you want the projectile to move both to X and Y directions, right?

With the formula based on Wikipedia you can solve the velocity from distance formula when both start and end y coordinates are 0. v = 1 / cos(a) * sqrt((0.5 * g * d * d) / (d * tan(a))); where v = velocity, d = distance, g = gravity and a = angle.

Distance you get by d = sqrt(pow((x2 – x1)) + pow(y2 – y1)), or Vector3.Distance(start, end) or (start,end).magnitude (where start and end are Vector3 objects)

So in our example: d = 15.0, a = 66.0, g = 9.81, replacing the variables in the equation give us v = 14.0716. This is our total velocity which needs to be used in 66 angle to hit the target. So how you apply this force to a rigid body?

Rigid body has a function AddForce() which have multiple overrides but basically it accepts velocity vector to be used as force. You can also choose what type of force you are interested applying to. Since it requires a vector or 2 to 3 components (x,y) or (x,y,z) we need to calculate how this 66 degrees and velocity are in terms of a vector.

First we get the horizontal component from the velocity. This is done by multiplying cos(angle) with total velocity. And vertical component you multiply with sin(angle). We also need to know what is the direction we want the force to be applied to. This you get by target position – source position normalized. Vector3 direction = (target.transform.position – source.transform.position).normalized; What this does it gives the direction without the length.

For Y component it’s always a positive so we don’t need to do anything. (We are firing upwards, right?) For X component we need to multiply the direction.x with horizontal velocity. Like this: float xDir = direction.x * horizontal; These are the values for your velocity vector which you can apply to the rigid body.

So this is the code:

var distance = Vector3.Distance(target.transform.position, position);
var velocity = (float)(1 / Mathf.Cos(angle * Mathf.Deg2Rad)) * Mathf.Sqrt((float)(0.5f * 9.81 * distance * distance) / (distance * Mathf.Tan(angle * Mathf.Deg2Rad)));
float horizontal = Mathf.Cos(angle * Mathf.Deg2Rad) * velocity;
var direction = (target.transform.position - position).normalized;
var rbVelocity = new Vector2(direction.x * horizontal, Mathf.Sin(angle * Mathf.Deg2Rad) * velocity);
rigidBody.AddForce(rbVelocity, ForceMode.Impulse);

Some stuff outside the code: I have local reference to rigidBody of the projectile which I have instantiated at source position and I am applying this force once when I instantiate the projectile. This can be done several ways but I did mine on left mouse click so I would be able to see multiple projectiles flying from source to target.

Now you have code for a projectile using rigid body flying constantly to the target position with a certain angle and range. Nice. What do you think of this? Do you have any comments, ideas or improvements? Next time we do this same exercise by calculating things manually.

–small after post note–

If you run into problems where the projectile do not land correctly even if you think the formula and calculations are correctly, this might be caused by Unity’s physics engine with too few velocity solving iterations. My projectile hit the target always when angle was more than approx 30 degrees but would always go over the target when lower. If your calculations are correct then try to add the “solver velocity iterations” in the project settings, in physics tab.