Getting slightly off results using AddForce or using Unity's gravity

Hi,

I have a problem with my trajectory calculations not matching Unity’s physics simulations.

I am working on a trajectory estimator. As I had trouble with a complex ballistic case, I wanted to understand where I was wrong and restarted simple.

I now have a simple case of a ball falling. Initial position is (0;0), initial speed is (0;0) and the only force applied on the ball is its own weight (mass * gravityAcceleration, with gravity acceleration = 9.81 m.s-2) and there is no drag at all.

So, if I am not mistaken, I have a trajectory equation like :
Y_Ball_Position(time) = - 0.5 * gravityAcceleration * time^2

In the meantime, I created a ball, gave it a rigidbody with a mass of 1kg and no drag.
I attached this script to it :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test1 : MonoBehaviour
{
    public float gravityAcceleration;
    private Rigidbody _ballRb;
    private float _mass;
    private bool fall = false;
    private float _timer = 0;
    private void Start()
    {
        gravityAcceleration = Vector3.Magnitude(Physics.gravity);
        _ballRb = this.GetComponent<Rigidbody>();
        _ballRb.isKinematic = true;
        _mass = _ballRb.mass;
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Z))
        {//Triggers the fall
            fall = true;
        }

        if (Input.GetKeyDown(KeyCode.E))
        {//Resets ball position to (0,0,0)
            _ballRb.isKinematic = true;
            _ballRb.useGravity = false;
            this.transform.position = Vector3.zero;
            _timer = 0;
            fall = false;
        }
    }
    private void FixedUpdate()
    {
        if (fall)
        {
            _ballRb.isKinematic = false;
            _ballRb.useGravity = true;
            //_ballRb.AddForce(-_mass * gravityAcceleration * Vector3.up);
            Debug.Log("time(s) = " + _timer + "    |    y(m) = " + this.transform.position.y + "    |    My_y(m) = " + -gravityAcceleration * _timer * _timer / 2); // formula is -0.5gt^2
            _timer += Time.fixedDeltaTime;
        }
    }
}

And here are the results I get :

So “time(s)” is the time elapsed since the beginning of the fall,
“y(m)” is the y position of the ball in the world at time = “time(s)”, based on Unity’s physics
And “My_y(m)” is the estimated position of the ball in the world at time “time(s)”, based on my calculations.

We can see that there is a small difference, but a difference noticeable as I want to achieve high precision.

I also tried with an AddForce to apply the weight myself (see lines 40-41)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test1 : MonoBehaviour
{
    public float gravityAcceleration;
    private Rigidbody _ballRb;
    private float _mass;
    private bool fall = false;
    private float _timer = 0;
    private void Start()
    {
        gravityAcceleration = Vector3.Magnitude(Physics.gravity);
        _ballRb = this.GetComponent<Rigidbody>();
        _ballRb.isKinematic = true;
        _mass = _ballRb.mass;
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Z))
        {//Triggers the fall
            fall = true;
        }

        if (Input.GetKeyDown(KeyCode.E))
        {//Resets ball position to (0,0,0)
            _ballRb.isKinematic = true;
            _ballRb.useGravity = false;
            this.transform.position = Vector3.zero;
            _timer = 0;
            fall = false;
        }
    }
    private void FixedUpdate()
    {
        if (fall)
        {
            _ballRb.isKinematic = false;
            //_ballRb.useGravity = true;
            _ballRb.AddForce(-_mass * gravityAcceleration * Vector3.up);
            Debug.Log("time(s) = " + _timer + "    |    y(m) = " + this.transform.position.y + "    |    My_y(m) = " + -gravityAcceleration * _timer * _timer / 2); // formula is -0.5gt^2
            _timer += Time.fixedDeltaTime;
        }
    }
}

But I got exactly the same results.

It seems that the weight is applied twice on the first FixedUpdate, then it is applied normally but it still carries an offset due to the twice force application in the first FixedUpdate.

Can anybody tell me what I am doing wrong?

In the first script the formula is fine. You won’t get high precision when using floating point numbers. The further ahead in time you wish to predict the more inaccurate the prediction will be. You can either regularly update the predicted position throughout the ball’s flight path or just update the position at the end by simply setting the predicted position to the ball’s final position.

If you choose to update the position regularly throughout the ball’s flight path then you’ll need to also take the ball’s velocity into account.

With your second script you should’ve used ForceMode.Acceleration and left out the mass on line 41.

Hi, thank you for your reply.

1 : I don’t want to “manually” make the ball move as the game is a “basket ball” game, having multiple balls thrown and obstacles interacting, so I don’t want to start checking for collisions, get normals, velocities, masses… and recalculate their trajectories. I can make it but I’m lazy :slight_smile: In fact, I am just aiming at creating a short previsualisation of the shot to help players see where they will launch the ball (like in Angry Birds for instance). And yes I know, I could directly use the velocities, the equations require one integral less and are easier but I don’t want to, I want to exercise with forces. Moreover I am almost there, I already achieved it, the ball was going in the “right direction”, but with a slight offset right at the beginning.

So back to my simple falling ball problem. The problem is that the difference is visible and occurs at fist frame. The results on the picture show that on frame one, no matter if its script 1 or 2 which is used :

  • The calculated position is : 0.001962 m
  • The Unity position is : 0.003924 m, which is exactly twice the expected result

On later frames, the results are “fine” as the calculated positions and Unity ones tend towards each others, despite still carrying this slight offset from 1st frame.

So my guess is that at the first fixed update, Unity applies twice the weight. So I tried different things to try to see what in my code was causing this (avoided using Rb.IsKinematic, put the inputs code in the fixedUpdate…) but still the same results.

2 : I tried with forceMode.Acceleration but still, I get the same results. However, could you explain to me why it is more appropriate to use forceMode.Acceleration instead of forceMode.Force in my case. I looked up the documentation and don’t see major differences

ForceMode.Force includes mass. ForceMode.Acceleration ignores mass which is what you need when simulating gravity.