fixedDeltaTime.... I thought I understood it, but I clearly don't....

This is so embarrassing… I know there are lots of posts and tutorials about this, and I thought I understood it, but… I must be an idiot.

I thought that if you multiply .addForce with Time.FixedDeltaTime, it would make the velocity of the object independent of the physics timestep. So, I thought, the following code (which is in FixedUpdate) would make an object move at the same speed no matter what the timestep is…

float finalThrust = thrust - thrustTurningAdjust + isotropicBoost;
finalThrust = Mathf.Clamp(finalThrust,0, 250);
_rigidBody.AddRelativeForce(Vector3.forward * (finalThrust * Time.fixedDeltaTime ));
Debug.Log("velocity " + _rigidBody.velocity.magnitude

but it doesn’t, the higher the timestep, the faster the object moves, and I don’t understand why…

Just remove Time.fixedDeltaTime from the calculations and the force will be independent of the timestep.

A force is just a force magnitude in Newtons. Internally, the physics engine integrates the force over the current time step to calculate the new state of the Rigidbody.

In your code, you’re just multiplying the force magnitude by an arbitrary number, 0.02 by default. So if you double the time step, this number becomes 0.04 and the force gets doubled.

2 Likes

Time.deltaTime is used for example when calculating positions out of velocities, or velocities out of accelerations, as the resulting value depend on the delta time:

pos1 = pos0 + vel * deltaTime

Also, note that Time.deltaTime can be used anywhere. When used from inside FixedUpdate, Time.deltaTime already returns the value of Time.fixedDeltaTime.

Thanks for your reply Edy.
I’ve removed the multiplication with Time.FixedDeltaTime. So the line looks like this.

_rigidBody.AddRelativeForce(Vector3.forward * (finalThrust));

Now the velocity of the objects scales according to the Fixed Timestep setting. If I set Fixed Timestep to 0.01 then the object moves at half the speed it would if Fixed Timestep is set to 0.02.
This is exactly the opposite to what I’d expect.

I thought that “fixed timestep” was the frequency at which FixedUpdate would be called. Therefore every fixedUpdate finalThrust would be added to the objects velocity. So, the lower FixedUpdate is set, the more frequently force would be added to the object so, overall, more force. Therefore, if you multiplied the force by FixedUpdate, (the more frequent the update the lower the multiplier), it would cancel out and be a consistent force, no matter how frequently fixed update ran.

If FixedUpdate is 0.02 that means it’s called 50 times per second.
If force is 10 and it’s added 50 times per second you get 500 units of force per second.
So when FixedUpdate is set to 0.01 it’s called 100 times per second.
If force is 10 and its added 100 times per second then that should be 1000 units of force.
So (in my mind) the second one should be faster, but it’s not, its slower.

In my mind you should be able to multiply the force by the frequency, to end up with the same overall force per second.
10 * 0.02 * 50 = 10
10 * 0.01 * 100 = 10

But that’s not what I’m seeing. As I said, the higher the Fixed Timestep (lower frequency?!) the faster the object moves. In fact it seems to cancel out when I divide the force by the frequency. Which makes no sense to me at all.

(10 / 0.02) * 50 = 25000
(10 / 0.01) * 100 = 100000

Yet somehow, this makes the object move at a consistent speed and different Fixed Timesteps.

I must be making myself look very stupid here, I’m obviously misunderstanding something very dumb. Any idea what it is?

Then there’s something wrong in your code. Some ideas to check:

  • All the code from the first post must be executed within FixedUpdate().
  • Ensure you’re not using Time.deltaTime (or Time.fixedDeltaTime) in any calculation that reaches finalTrust (i.e. thrust, thrustTurningAdjust, isotropicBoost).

Create a brand new scene with this:

  • A cube or sphere with a Rigidbody of 1 Kg of mass. Disable “Use Gravity”. Play. The cube should stay quiet in the void.
  • A script with this line in FixedUpdate(): rigidbody.AddForce(Vector3.forward * 10).

Now the cube will accelerate 10 meters per second each second no matter the value of Fixed Timestep.

No, that’s not how it works. Both situations produce the same result. The force is not “added” 50 or 100 times per second, but it’s integrated 50 or 100 times per second. The difference is that the integration happens for the duration of the timestep. The result is invariant, just more precise (i.e. rapidly changing forces are better handled with more steps).

Simplifying things a lot, here’s what the physics engine does on each fixed timestep for each rigidbody:

  • Received force: F
  • Calculate acceleration: a = F / mass
  • Calculate velocity: v = v + a * Time.deltaTime

So the resulting velocity is invariant of Time.deltaTime for the same force value. When the fixed timestep is 0.02 this process happens 50 times per second. When the fixed timestep is 0.01 this process happens 100 times per second but with half Time.deltaTime, so the resulting velocity after 1 second is exactly the same in both cases.

Note: when read from FixedUpdate, Time.deltaTime returns the value of Time.fixedDeltaTime. The best pratice is using Time.deltaTime in both Update and FixedUpdate.

2 Likes

Hey, thanks so much for taking the time go go through this so clearly with me.
I think I found the root of my problem in the end. Elsewhere in the code was applying some force using ForceMode.VelocityChange

To achieve this, so that any given frame rate and/or any given rate of fixedUpdate produces the same rates of movement/acceleration, which update and Time.____ should be used?

Rigidbody.AddForce from FixedUpdate. Don’t use any delta time in the calculations of the force.

Also:

1 Like

So it would be better to think of AddForce as “Apply apportioned continuous Force…”
?

No. Calling AddForce from FixedUpdate is just “Apply a continuous force”. The magnitude of the force in Newtons is the magnitude of the force vector passed to AddForce. As long as you’re passing the same values to AddForce, the result will be independent of the physics rate.