How to calculate force needed to jump towards target point?

Hi all,

So things that I know/got:

  • my object’s position (start position) and the target position (end position)
  • direction (target - my object position ?)

What I need to know is the force that is required to get/jump to the target position (rigidbody.AddForce ( requiredForce * direction, ForceMode.Impulse);

I have already tried to look around and found the following threads:

They calculate the arc but that is not exactly what I need.

It would be great if someone knows the calculation for this and can explain if it is a difficult formula (I’m not that good with physics calculation).

Thanks.

3 Likes

I don’t use rigidbodies, but the same principles should apply in the code below:

// Calculate the initial velocity of a jump based off gravity and desired maximum height attained
private float CalculateJumpSpeed(float jumpHeight, float gravity)
{
    return Mathf.Sqrt(2 * jumpHeight * gravity);
}

…and you plug in the height you want to hit with your jump, and your current gravity magnitude.

1 Like

I tested it, but I could guess that this is not exactly what I need.

   void JumpTowardPoint()
    {
        float gravity = Physics.gravity.magnitude;
        float initialVelocity = CalculateJumpSpeed(jumpHeight, gravity);

        Vector3 direction = target.transform.position - transform.position;

        rigidbody.AddForce(initialVelocity * direction, ForceMode.Impulse);
    }

    private float CalculateJumpSpeed(float jumpHeight, float gravity)
    {
        return Mathf.Sqrt(2 * jumpHeight * gravity);
    }

This will shoot just shoot my object in the direction of the target and just fly over it. What I actually want is that it ‘jump’/go to that target spot.

2 Likes

Hey! Sorry if I didn’t understand right. So what you’re looking for is essentially a “cannon-ball” type problem, where you are firing launching your player (the cannonball) at the target…like this?

It’s pretty late but if that’s the idea, I’ll write up a more detailed answer tomorrow.

3 Likes

Yes, that’s what I mean. Sorry if I wasn’t all too clear with my explanation.

Okay, so the first thing to bear in mind is that the goal to solving this is to treat the object’s motion along the two axes separately, which we’ll call x and y. Although the results of this can be used in 3d space, this problem only concerns two axes, which is nice. Our given values (the ones we have) are the position of our object, position of the target, and the angle that we want to fire at. From this, we know the planar distance between the object (d) as well as the offset on the y axis between the two. We want the initial velocity to fire the object at to strike the target t.

As stated above, we’ll find the velocity of the components, x and y. vX (velocity x) is constant (there’s no air resistance or anything) and so is the following:

v0 * cos(angle)

…where v0 is our initial velocity. We know that the distance an object travels is equal to it’s velocitytime, so the distance x travels is vXt, or how long our object is in the air.

vY is just as easy to solve for, giving us:

v0 * sin(angle)

However, y is not a constant velocity, but rather has the acceleration of gravity on it throughout it’s time in the air. So the velocity of y is equal to the initial velocity, PLUS however much acceleration has acted on it over the course of the time in the air.

vY = -gt + v0 * sin(angle)

So as you can see, at t=0, y’s velocity is just the initial velocity, but over time more acceleration is added. Our y position is then:

-1/2*g*t^2 + v0 * sin(angle)*t + y0

Now that we can calculate the position of our object on either two axes at any time, we can create a formula to solve for initial velocity. Our initial position is equal to x0 = 0, and y0 = yOffset. We also know that the final x position we want is just equal to distance, and the final y position is equal to 0. We can insert all these into the above two equations, and solve for the both of them (which eliminates t) to get the following:

v0 = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));

It’s a bit to take in! I had to figure this our a few years back for a project, and I got most of the info from this physics exchange post. Implementing the above in Unity looks like this:

using UnityEngine;
using System.Collections;

public class ProjectileFire : MonoBehaviour {

    [SerializeField]
    Transform target;

    [SerializeField]
    float initialAngle;

    void Start () {
        var rigid = GetComponent<Rigidbody>();

        Vector3 p = target.position;

        float gravity = Physics.gravity.magnitude;
        // Selected angle in radians
        float angle = initialAngle * Mathf.Deg2Rad;

        // Positions of this object and the target on the same plane
        Vector3 planarTarget = new Vector3(p.x, 0, p.z);
        Vector3 planarPostion = new Vector3(transform.position.x, 0, transform.position.z);

        // Planar distance between objects
        float distance = Vector3.Distance(planarTarget, planarPostion);
        // Distance along the y axis between objects
        float yOffset = transform.position.y - p.y;

        float initialVelocity = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));

        Vector3 velocity = new Vector3(0, initialVelocity * Mathf.Sin(angle), initialVelocity * Mathf.Cos(angle));

        // Rotate our velocity to match the direction between the two objects
        float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion);
        Vector3 finalVelocity = Quaternion.AngleAxis(angleBetweenObjects, Vector3.up) * velocity;

        // Fire!
        rigid.velocity = finalVelocity;

        // Alternative way:
        // rigid.AddForce(finalVelocity * rigid.mass, ForceMode.Impulse);
    }
}

Which also rotates our velocity so you can fire in any direction, since we only solve it for the x and y plane.

37 Likes

Thanks for the explanation and how to implement. I tested it out but I encountered the following 2 scenarios:

  • by using rigidbody.velocity, after the projectile object fires and the x and z are equal to the x and y of the target, the object will slowly goes down (which isn’t what we want because of physics)
    -by using the addforce, the projectile object will shoots up and keeps the force applied to it(?). So it will never hit the target?

I think that I need a way to find when the projectile has reached the max height of the trajectory and set the velocity back to 0 or make it decrease? I’m not really confident with my physics knowledge but because of gravity, after reaching the max height, shouldn’t the gravity stop the acceleration of the force and bring it back to 0 and make the object fall down again?

My object did not ‘look’ in the direction of the target (instead of the blue arrow in unity, the red arrow was pointing towards the target). Do I have to set the rotation myself like this?

Vector3 rotateValue = target.transform.position - transform.position;
        Quaternion lookRotation = Quaternion.LookRotation(rotateValue);
        transform.rotation = lookRotation;

2 Likes

Hmm, not entirely sure what you’re asking. The ball is “pushed” back down to zero by Unity’s built in gravity (since you’re using a rigidbody). Here’s a gif of the function being run, with three different initial values. Was the script not working?

4 Likes

No idea what I did wrong yesterday but it works now. Thanks for the help.

@Iron-Warrior Thanks for the in-depth explanation, it worked like a charm! =)

2 Likes

@Iron-Warrior Thanks for the code and the explanation. I just have one question: was supposed to work all directions? When I use here, if the object is on the rigth of the target, for example, the force is inverse, and the object go to rigth, instead go to left, where is the target.

1 Like

While his code is very helpful, it wasn’t perfect. To be a bit more specific, when the velocity needs to push the object along the world space X axis, is was unable to apply a negative X velocity, resulting in the vector never being able to go left (relative to world space).

To fix this, check if the object position x is greater than the target position x (i.e. check if going in a negative X direction is required to reach the target) and, if so, inverse the angleBetweenObjects (i.e. mulltiple by -1).

Before

Code Change

// From
float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion);
// To (added "* (p.x > transform.position.x ? 1 : -1)")
float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion) * (p.x > transform.position.x ? 1 : -1);

After

20 Likes

This thread is pretty old, but does someone know how to increase the speed of the jump using the script from Iron-Warrior without changing the destination. If you increase the addforce, it won’t go to the desired destination, but I want the time of the jump to be shorter. I can’t use timeScale because the other objects shouldn’t go faster.

2 Likes

Iron-Warrior’s script calculates the velocity of the projectile based on an initial angle and the target destination. The only physical way for it to get their faster is to use a smaller (flatter) angle.

But you could try using an alternate gravity force. If you use a stronger gravity it will get there faster. If you don’t want to adjust the global gravity you could specify the gravity force in the script, but you’d have to disable gravity on the rigidbody and do your own gravity in fixed update.

Edit:
Or maybe you’re looking to start with an initial velocity and want to figure out the angle need to hit a target? I think I’ve done that before but I can’t remember how to do it at the moment.

2 Likes

Thnx. I changed the global gravity to -100 and the speed is okay now. I don’t know if this gravity is bad?

Hi @Zethros & @Iron-Warrior , thank you very much for sharing the codes.
Just wondering how do I draw the dotted line that sort of predicts where the ball will travel before shots ?
similar to Angry birds before you release the catapult.

Sorry, but I never coded something like that before. I’ll leave that answer to someone else

(Sorry for the delayed response. I didn’t get a notification from you mentioning me for some reason.)
(Also sorry for replying without an answer. I just didn’t want people who could answer to think they shouldn’t because you asked Iron-Warrior and I so specifically)

Maybe let a gameobject automatically jump to the destination with a trail behind it?

If anyone is interested, I was planning on writing an article about projectile trajectories but it’s kind of fell on the wayside due to other obligations. Anyways, I made an example project for the article that allows you to fire a projectile at either a fixed angle or a fixed velocity and draws out that trajectory.

Bang bang. Anyways, it mostly boils down to this ProjectileMath class I wrote:

using UnityEngine;

public static class ProjectileMath
{
    public static bool LaunchAngle(float speed, float distance, float yOffset, float gravity, out float angle0, out float angle1)
    {
        angle0 = angle1 = 0;

        float speedSquared = speed * speed;

        float operandA = Mathf.Pow(speed, 4);
        float operandB = gravity * (gravity * (distance * distance) + (2 * yOffset * speedSquared));

        // Target is not in range
        if (operandB > operandA)
            return false;

        float root = Mathf.Sqrt(operandA - operandB);

        angle0 = Mathf.Atan((speedSquared + root) / (gravity * distance));
        angle1 = Mathf.Atan((speedSquared - root) / (gravity * distance));

        return true;
    }

    /// <summary>
    /// Calculates the initial launch speed required to hit a target at distance with elevation yOffset.
    /// </summary>
    /// <param name="distance">Planar distance from origin to the target</param>
    /// <param name="yOffset">Elevation of the origin with respect to the target</param>
    /// <param name="gravity">Downwards force of gravity in m/s^2</param>
    /// <param name="angle">Initial launch angle in radians</param>
    public static float LaunchSpeed(float distance, float yOffset, float gravity, float angle)
    {
        float speed = (distance * Mathf.Sqrt(gravity) * Mathf.Sqrt(1 / Mathf.Cos(angle))) / Mathf.Sqrt(2 * distance * Mathf.Sin(angle) + 2 * yOffset * Mathf.Cos(angle));

        return speed;
    }

    public static Vector2[] ProjectileArcPoints(int iterations, float speed, float distance, float gravity, float angle)
    {
        float iterationSize = distance / iterations;

        float radians = angle;

        Vector2[] points = new Vector2[iterations + 1];

        for (int i = 0; i <= iterations; i++)
        {
            float x = iterationSize * i;
            float t = x / (speed * Mathf.Cos(radians));
            float y = -0.5f * gravity * (t * t) + speed * Mathf.Sin(radians) * t;

            Vector2 p = new Vector2(x, y);

            points[i] = p;
        }

        return points;
    }
}

You can get the full project at the GitHub repo here.

27 Likes

Sebastian Lague have some good videos on kinematic equations, where one of them is related to this specific problem.

3 Likes