Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When do you multiply by Time.deltaTime in Unity?

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:

  • Doing rigidbody.velocity += acceleration*Time.deltaTime; in FixedUpdate. Since FixedUpdate runs at a constant timestep, does this make the multiply by Time.deltaTime unnecessary?
  • Doing rigidbody.AddForce(force*Time.deltaTime, forceMode);. How do the various ForceModes 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.

like image 869
Eric Pendergast Avatar asked Dec 07 '22 10:12

Eric Pendergast


2 Answers

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.

like image 121
Eric Pendergast Avatar answered Dec 13 '22 03:12

Eric Pendergast


A simple way to think about this is to determine:

Are objects' transforms manipulated directly?

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().

Are objects being manipulated with physics?

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).

like image 32
rodamn Avatar answered Dec 13 '22 03:12

rodamn