Hi all,
I don’t understand how Unity applies forces. After I had trouble on a project with slight offsets between Unity’s results and mines, I decided to make a simple study case to understand where was the problem comming from. What I found is that on the first FixedUpdate, the AddForce seems to be applied twice, and I don’t understand why.
So, my study case is a ball falling down, with mass 1kg, initial speed 0m.s-1 and only affected by gravity with g = 10m.s-2 (no linear/angular drag)
I compare the balls’transform position with analytic ones obtained with y(t) = -0.5 * gt^2
So, now the results :
1 : When applying on the ball’s rigidBody an addforce of (g * mass) every fixedUpdate frame, I have slightly off results. I notice that there is an exact 2 times difference between the Unity and analytic results. Afterwards, the results tend toward each other, but still carrying the slight offset from the first frame.
public LineRenderer _lineRenderer;
[SerializeField] int nPoints = 10;
[SerializeField] float tIncrement = 0.02f;
[SerializeField] float gravityAcceleration = Vector3.Magnitude(Physics.gravity);
Rigidbody _rb;
float _mass;
bool _startFall = false;
bool _falling = false;
void Start()
{
_rb = GetComponent<Rigidbody>();
_mass = _rb.mass;
_lineRenderer.positionCount = nPoints;
_lineRenderer.SetPosition(0, Vector3.zero);
for (int i = 1; i < nPoints; i++)
{
_lineRenderer.SetPosition(i, Vector3.down * gravityAcceleration * Mathf.Pow(i * tIncrement, 2) * 0.5f);
}
}
private void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.A))
{
_falling = !_falling;
}
if (_falling)
{
Debug.Log("UnityPos = " + this.transform.position.y);
_rb.AddForce(gravityAcceleration * _mass * Vector3.down);
}
if (!_falling)
{
this.transform.position = Vector3.zero;
_rb.velocity = Vector3.zero;
}
}
2 : So, I tried applying an addforce of (g * mass) / 2 the first fixedUpdate frame, and (g * mass) on all other frames, I get perfectly identical results between Unity and analytic
public LineRenderer _lineRenderer;
[SerializeField] int nPoints = 10;
[SerializeField] float tIncrement = 0.02f;
[SerializeField] float gravityAcceleration = Vector3.Magnitude(Physics.gravity);
Rigidbody _rb;
float _mass;
bool _startFall = false;
bool _falling = false;
void Start()
{
_rb = GetComponent<Rigidbody>();
_mass = _rb.mass;
_lineRenderer.positionCount = nPoints;
_lineRenderer.SetPosition(0, Vector3.zero);
for (int i = 1; i < nPoints; i++)
{
_lineRenderer.SetPosition(i, Vector3.down * gravityAcceleration * Mathf.Pow(i * tIncrement, 2) * 0.5f);
}
}
private void FixedUpdate()
{
if (Input.GetKeyDown(KeyCode.A))
{
//Debug.Log("aaa");
_falling = !_falling;
if (_falling) { _startFall = true; }
}
if (_falling)
{
Debug.Log("UnityPos = " + this.transform.position.y);
if (_startFall)
{
Debug.Log("YYY");// to check how many times the force is applied
_rb.AddForce(gravityAcceleration * _mass * 0.5f * Vector3.down);// weight force divided by 2
_startFall = false;
}
else
{
_rb.AddForce(gravityAcceleration * _mass * Vector3.down);
}
}
if (!_falling)
{
this.transform.position = Vector3.zero;
_rb.velocity = Vector3.zero;
}
}
Does anyone have an idea of why only the first frame is “miscalculated” and/or what I am missing?
Ah and i know that a getkeydown in a fixedUpdate is stupid, but in this case it was to be sure that I only apply force once, without juggling between update to get inputs and fixedupdate to apply the forces. By the way I first tried this way and had the same problem, so it doesn’t come from it.