There seems to be a lot of confusing/conflicting advice out there about when you should or should not multiply a quantity by Time.deltaTime
. So I was wondering, as it pertains to the Unity physics system, when should you multiply by Time.deltaTime
?
I understand that Time.deltaTime is used to make certain actions framerate independent, but there are some situations where it isn't clear whether an action is framerate independent. For instance:
rigidbody.velocity += acceleration*Time.deltaTime;
in FixedUpdate. Since FixedUpdate runs at a constant timestep, does this make the multiply by Time.deltaTime
unnecessary?rigidbody.AddForce(force*Time.deltaTime, forceMode);
. How do the various ForceMode
s effect this?I've seen solutions to individual situations, but it would be nice if there were a simple intuition which could address all of these situations.
Multiplying by Time.deltaTime
is used to make an instantaneous operation act in a continuous (or "smooth") way.
An instantaneous operation will cause a certain quantity to "jump" to a different value. The following operations are instantaneous:
Rigidbody.[position|velocity|rotation|angularVelocity] += x;
Rigidbody2D.[position|velocity|rotation|angularVelocity] += x;
Rigidbody.Add[Force|Torque](x, ForceMode.[Impulse|VelocityChange]);
Rigidbody2D.Add[Force|Torque](x, ForceMode2D.Impulse);
All the above operations cause a quantity to instantly jump by x
, irrespective of the length of the frame.
In contrast, the following operations are continuous:
Rigidbody.Add[Force|Torque](x, ForceMode.[Force|Acceleration]);
Rigidbody2D.Add[Force|Torque](x, ForceMode.Force);
Rigidbody.velocity = x;
(continuous from the perspective of Rigidbody.position
. i.e. setting the velocity will not make Rigidbody.position
suddenly jump to a new value)All the above operations are applied over the course of the frame (at least conceptually this happens; what the physics system does internally is out of the scope of this answer). To illustrate this, Rigidbody.AddForce(1, ForceMode.Force)
will cause a force of 1 newton to be applied to an object for the entire frame; there will be no sudden jumps in position or velocity.
Time.deltaTime
?If you are using a continuous operation, you should almost definitely not be multiplying by Time.deltaTime
. But if you are using an instantaneous operation, the answer depends on if the operation is being used in a continuous way.
void Explode() {
rigidbody.velocity += explosionAcceleration;
}
Here you should not be multiplying by Time.deltaTime
since Explode()
gets called just once. It is not meant to be applied over a series of frames.
void Update() {
rigidbody.position += velocity * Time.deltaTime;
}
Here you do have to multiply by Time.deltaTime
because the object is supposed to move continuously over time, and you are also using an instantaneous operation. Note that this could be replaced with a continuous operation, and remove the need for multiplying by Time.deltaTime
:
void Update() {
rigidbody.velocity = velocity;
}
Although this isn't entirely equivalent to the original, since it ignores the previous value of rigidbody.velocity
.
void Update() {
rigidbody.AddForce(acceleration*Time.deltaTime, ForceMode.VelocityChange);
}
Here you should be multiplying by Time.deltaTime
because you want the velocity to increase at a consistent rate frame by frame. Note that this is precisely equivalent to:
void Update() {
rigidbody.AddForce(acceleration, ForceMode.Acceleration);
}
This code applies the acceleration over the course of the whole frame. This code is also (in my opinion) cleaner and easier to understand.
FixedUpdate
?Being in FixedUpdate
does not effect any of the above advice. Yes, you could theoretically get away with never multiplying by Time.deltaTime
since the time between frames would always be the same. But then all your units would have to be dependent on the fixed frame rate. So for example if you wanted to move at a rate of 1 m/s
with 60
frames per second, you would have to add 1/60=.01666
to the position each frame. And then imagine what happens if you change your fixed timestep. You would have to change a bunch of your constants to accommodate.
Executing code in FixedUpdate
does not suddenly make that code framerate independent. You still have to take the same precautions as in Update
.
As a side note, you do not need to replace Time.deltaTime
with Time.fixedDeltaTime
inside FixedUpdate
because Unity already does this substitution.
Only multiply by Time.deltaTime
if you want an instantaneous operation to act smoothly over a series of frames.
Many times, you can avoid using Time.deltaTime
by using continuous operations, which are often more intuitive (see examples 2 and 3). But this of course depends on the context.
A simple way to think about this is to determine:
When an object is being moved directly via its transform.position
and transform.rotation
components, manipulation of the transform should occur in Update()
, which runs once per drawn frame. The developer should build their game with the expectation that the Update/frame rate varying between zero and the game's maximum frame rate. The way to accomplish this is by factoring in the time between now and previous frame into the movement (step) for each update/frame.
Thus, manipulations to the transform.position
and transform.rotation
should factor in Time.deltaTime
and should occur within Update()
.
In this case, forces and torques should be applied by calling Rigidbody.AddForce()
or Rigidbody.AddTorque()
in FixedUpdate()
. FixedUpdate()
can be expected to occur at a fixed/guaranteed rate, which Unity defaults to 50 times per second (this can be adjusted within the Physics settings of your game project).
To complicate things a bit, when AddForce()
and AddTorque()
are called with ForceMode.Force
(default) or ForceMode.Acceleration
, the value provided for the force
or torque
parameters will be interpreted as amounts for one entire second, so these functions will adjust the value (a force/torque or acceleration/angular acceleration) for one fixed time step (i.e., x 0.02s with the default Unity Physics fixed update rate).
In case you are curious about ForceMode.Impulse
or ForceMode.VelocityChange
, these will interpret the force
or torque
as the amount to apply within that fixed time step (i.e. the value will not be adjusted with respect to time).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With