The intuition
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.
So when do you multiply by 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.
Example 1: Explosion
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.
Example 2: Moving
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
.
Example 3: Accelerating
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.
What about 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.
Conclusion
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.