You're watching a tutorial on player movement. "...Multiply the speed by time dot delta time," they say, "so that it's consistent across varying framerates."
You're watching a tutorial on custom gravity. "...Let's not forget to add delta time here."
You're watching a tutorial on shooting bullets. "Divide the distance by time dot delta time, and..."
Okay, we get it! Movement needs delta time. Fine.
We have a car, and we want to move it at a constant speed.
For simplicity's sake, let's forgo vectors and do things in one dimension: the number line. The car will start at position 0, and we want to increment that by 10 units every second.
We'll do that by directly adjusting the car's position—a common method when you want total control over its velocity, rather than entrusting it to the game's physics engine.
Let's say we create the
Update() function and write the following:
car.position += speed. We set
speed to 10 and run the game, but the car's way faster than intended. We dial it down to 0.2.
Everything seems okay now. The car's cruising along, but then, there's a sudden drop in FPS, and it slows to a halt. A second later, it's all back to normal.
In single-player, you might not find this strange: the game freezes and so does the car. But if you were racing against someone else online—let's call her Sarah—who was adjusting her own car's position on her own computer and experienced no such spike, it'd appear as if you screeched to a stop. Sure, you pick your speed back up immediately, but now you've fallen behind her.
The problem with our code is that it's executed every frame. We're not setting the car's speed to 0.2 units per second; we're setting it to 0.2 units per frame.
The reason the car was blazing fast in the beginning, before we adjusted
speed, is that the
Update() function is called dozens of times each second. If the game runs at 50 FPS, it means the car goes at 500 units per second, rather than the 10 we originally intended. When the framerate dropped to near-zero, a whole second passed without the car moving.
Now imagine you're back to racing in multiplayer, but your PC's beefier than your opponent's. Your game runs at a whopping 100 FPS instead of fifty. While Sarah's trudging along at 10 units per second (
0.2 * 50 = 10—remember, we set
speed = 0.2), you leave her coughing up dust with your 20 units per second (
0.2 * 100 = 20). Sure, it feels good, but it's not very fair.
This is where delta time comes in. Delta time is the time it took to render the frame. Another way to put it: delta time is the time period between last frame and the current.
Delta means "difference".
Assuming a consistent framerate of 50, delta time will always be
1 / 50 = 0.02 seconds per frame. How can we use this? It's the same thing we did in our high-school physics classes.
When we write
car.position += 10 * Time.deltaTime, we write
(10 units) * (0.02 seconds / frame), or
0.2 units * seconds / frame. Since the game runs at 50 FPS, why, that's the same as saying the following:
Frames and seconds cancel out, and 50 frames later, we're left with
0.2 * 50 = 10 units!
Of course, games rarely, if ever, run at a dead-steady FPS. But as framerate changes, so does delta time. Remember that sudden FPS drop you experienced? Since a whole second passed until the next frame, delta time in that next frame would be 1 second per frame—up from 0.02.
Instead of adding
speed * 0.02 to the car's position, the game would add
speed * 1, or 0.2. To your eyes, the car would appear to teleport forward after that spike, but that's how it would've moved if the spike had never happened in the first place, and you wouldn't fall behind Sarah.
Of course, now that we're scaling the speed with delta time, setting it to 0.2 would be awfully slow. That's because it represents the car's actual speed. We can set it to 10, confident the car will now always travel at 10 units per second, and not have to blindly grope for a value that "feels right".
Once you understand this, you realize how much more you can do with it. For example, I recently worked on a mechanic where you drag a ball around with your cursor and, when you let go, the ball goes flying.
The problem is that the ball will not, on its own, go flying. All I'd done was disable the physics and make it follow the mouse cursor when clicked. The moment the player let go, the ball would just... stop. And then physics would kick in again and gravity would take it down.
As such, I had to measure its velocity the instant I let go and assign it manually. The problem is there's no such thing as speed or velocity at some particular instant. By definition, an object's velocity describes change; change in position. It's measured by the time it took to cover a certain distance (recall:
v = S/t), and if there's no change, there's no velocity.
If you measure over, say, an hour, sure, you get a velocity, but it hardly describes the object at a particular instant. Rather, we want the time frame to be as short as possible.
The shortest we can get in games is how much distance the object covered in one frame. We record that, and the result is 0.2 units. So, its speed is 0.2 units per frame.
Unfortunately, the physics engine doesn't run on units per frame. It runs on units per second. We need to convert our number if we're to make use of it.
If we, again, assume a steady framerate of 50, delta time is 0.02 seconds per frame. We divide the 0.2 units per frame with 0.02 seconds per frame, giving us the following:
Ta-da! 10 units per second. Now we just have to assign it to
rigidbody.velocity, if you're using Unity).
Why'd we get a result in units in the first problem and in units per second in the 2nd?
Because, in the first problem, we were changing the position. Every frame, we needed to find the correct distance to advance with, such that it added up to ten after one second.
In the second problem, we were assigning velocity; once, when the player let go. Ergo, we needed not just distance (units) but speed (units per second).