Fix your (Unity) Timestep!

The preeminent article on game timesteps is Gaffer On Games: “Fix Your Timestep!”.Cached version, as the article occasionally goes down. The article gives a great amount of context for implementing a game engine time step in a couple of different ways. Unfortunately, while this is useful, it is aimed at those writing their own engine, not using an existing one.

On top of that, there is surprisingly little information regarding the precise behavior of the Unity time step. The best articles are in the manuals, but they stop at a simple description and don’t go into any subtle detail.

Thus, this article attempts to collate the varying information on the Unity time step in one place and add objective testing to illuminate its behavior in the corner cases. So without further ado!

The Unity Timestep

In this article, we will need to talk about different types of time:

wall time: The amount of time passing in the real world on your wall clock — Time.realtimeSinceStartup

game time: The amount of time that is perceived to pass within the game — Time.unscaledTime

Under heavy load (see below), game-time may slow down, and may not match one-to-one with wall-time.


Unity uses a two-part timestep, as described in the Gaffer article as “Free the physics”. In Unity, this means that there are two main update passes: FixedUpdate and Update.

The FixedUpdate pass steps forward in increments of 0.02 game-time secondsThe default is 0.02 seconds. You can change the fixed timestep amount by setting Time.fixedDeltaTime — even at runtime!, regardless of rendering or any other performance factors. The key here is that FixedUpdate is reliable. If 0.02 seconds of game time has passed, it is guaranteed that the FixedUpdate has run during that period. Note that this does not guarantee FixedUpdate will be run every 0.02 wall-clock seconds! This is only the case when game-time and wall-time are in sync.

The Update pass steps forward in leaps and bounds. It generally correct to think of this pass as the “render update”, as it runs exactly once per frame render. It steps forward in a dynamic number of game-time seconds. Unity attempts to run this Update pass at the given Application.targetFrameRate. This is generally 60 FPS, but is specialized depending on the platform and V-Sync rate.

Depending on the circumstances, multiple FixedUpdates may run between two Updates (the game is render-blocked), or multiple Updates may run between two FixedUpdates (rendering is faster than the FixedUpdate step).

By default on desktop, Unity runs the FixedUpdate at 50 FPS and the Update at 60 FPS (the VSync rate). As for the reasoning behind this choice, it seems that even the folks at Unity have long forgotten. But consider this. These numbers also happen to be values Gaffer uses for the example at the end of the article! Coincidence? We can only imagine.

Measuring Update/FixedUpdate

Lets test some of the above assumptions. We’ll use the fantastic Squiggle asset to log graphs of each of our tests.

To start, let’s run Unity under a simple scene and the default settings. The following graph shows FixedUpdate in blue, then Update in red. At the bottom is one second of wall-clock time.

Blue: FixedUpdate, Red: Update, Grey: 1 Second Wall-Clock

Blue: FixedUpdate, Red: Update, Grey: 1 Second Wall-Clock

Over the course of a single wall-time second, 52 FixedUpdates and 62 Updates passed. If game-time and wall-time were perfectly matched, we would expect 50 FixedUpdates and 60 Updates. However, the real world is a messy place, and instead Unity game-time is always catching up and overshooting the wall clock. We over-counted this second, but we’ll under count in some later second to balance it out. In practice, this minor fluctuation in game-time vs. wall-time is imperceptible.

Notice something interesting about the FixedUpdate graph. They seem to be grouped in smaller batches of updates. Within each batch, 20ms of game-time passes for each, and 17ms wall-time passes. Then, there is a gap where 20ms of game-time passes and 35ms of wall-time passes, before another batch gets run. Unity seems to be trying to run the slower FixedUpdate in lockstep with the faster Update until it must pause to maintain 50FPS.

On the other hand, Update is perfectly smooth. We can thank V-Sync for this. My display is set to 60Hz, and as such, Unity performs an Update whenever the display is ready for the next frame.

So what does this mean for stutter? In this setup, now and then two frames will be rendered for a single FixedUpdate pass. If the render frames are being sent at 60Hz, you’d see a single frame stutter every 5-8 frames. If you’re doing processing in FixedUpdate, without interpolating,Interpolation can fix this stutter to some degree, but has its own set of visual artifacts. you’ll see this fairly consistent stutter on playback. This is not great, and is a big reason developers in Unity feel pressured to do their processing in Update.

You can see this clearly in the following graph. Here, each FixedUpdate is assigned a color. The Update is colored with the FixedUpdate that preceded it.

2019-05-13_09-04-49.png

Look closely and you’ll notice duplicates in the Update track. Every 5-8 frames, two Updates render for a single FixedUpdate.

Luckily, though, Unity exposes the FixedUpdate time-step to developers. What if we try running the FixedUpdate at 60FPS instead of 50FPS?

FixedUpdate timestep at 60FPS, Update timestep at 60FPS

FixedUpdate timestep at 60FPS, Update timestep at 60FPS

Much better!

Fundamentally, there’s no way to run a 60 FPS Update and a 50 FPS FixedUpdate without incurring some artifacts. This is a sampling problem, and you have to pick your poison. The best option is to do your best to keep your FixedUpdate running close to your render timestep. Failing that, interpolation may be worth investigating.

If you take one thing away from this article, I would consider setting your Time.fixedDeltaTime to 1/60f.

A Heavy Load in FixedUpdate

Up to this point we’ve been dealing with Unity at its most performant. However, this is rarely the case in production. We’ve said that the FixedUpdate is guaranteed to run every 0.02 game-time seconds. What happens when the FixedUpdate pass takes longer than 0.02 wall-time seconds to run? In this case, by the time a single FixedUpdate runs, even more wall-time seconds have passed and it will need to run again! It will never catch up. Instead, Unity chooses to just slow down the overall game-time itself.

The following setup spends 33ms in every FixedUpdate, causing the game to take only 30 FixedUpdates per second. Thus, game-time progresses forward 0.6 seconds for every 1 wall-time second (30 FPS / 50 FPS) = 0.6.

Unity_2019-05-14_08-08-15.png

Notice here that Update is also lowered in frequency, even though the Update pass (and rendering) has no load! Even though the FixedUpdate is updating at 30 FPS, the game is only being rendered at a measly 6 FPS. You might expect that rendering would run once every FixedUpdate, but that’s not what happens. The reason behind this is due to Unity’s ‘catch-up’ mechanism.

When the Update perceives that game-time is running behind wall-time, it will run the FixedUpdate as fast as possible until it catches back up. This is sensible when your game is largely render-blocked (as FixedUpdates will be relatively quick). However, in this case, because the FixedUpdate is too slow to run at frame-rate, Unity would end up running FixedUpdates forever, doomed to never catch up.

Instead, Unity provides a ‘bail out’ setting: Time.maximumDeltaTime. After a certain amount of time spent of “catching up” on FixedUpdates, it will bail out to finish the frame before running any more. By default this value is set to 0.1 seconds (wall-time). This explains the 5 FixedUpdates between each render. If we like, we can lower this value down, such that it always bails out after one FixedUpdate. Here is the same scene with a setting of 0.02.

Unity_2019-05-14_08-48-52.png


Much smoother. Now there’s one Update for every FixedUpdate (no sampling issues). Both run at 30 FPS. Unfortunately, tuning this value is tricky and depends on the game. Set it too high and you can see lower frame-rates than necessary. If you set it too low, the game could get bottlenecked rendering all of those extra frames (ie. Unity isn’t able to skip rendering to help catch up).

One more thing. In this last case, because the game is running slower than real-time, Unity is passing a Time.deltaTime of 0.02 game-time seconds to the Update function. This is a different value than the actual wall-clock frame time! Each Update pass actually spans about 0.033 seconds, but because the game as a whole has been slowed down, the deltaTime is scaled down as well. Try your best not to think of Update’s deltaTime as “the time between frames”. In many situations that’s not correct — more correctly it is the game-time between frames.

Slowing down is probably the best thing that Unity can do in this situation. Without dynamically changing the FixedUpdate timestep, there’s just no way to make the game do 0.033 seconds of work in 0.02 seconds. The lesson here is to be careful about the amount of work placed in the FixedUpdate (ie. Physics, Game State logic). A physics heavy scene will slow down the game, regardless of the ability of Unity to render at a dynamic frame rate.

A Heavy Load in Update

What if, instead, we are render-blocked or blocked on heavy animation / skinning on the CPU? In this case, the Update function will be slow. Here, I have set the Update function to run at 10 FPS with a 100ms second load.

Unity_2019-05-14_06-49-06.png

This result is a bit odd. We can count 10 Update passes for every wall-time second — this checks out. However, at first glance it seems like the FixedUpdate is only being run 10 times a second as well. If this were true, then the game physics would be proceeding at 1/5th the speed! If we zoom in, the reality becomes more clear.

Unity_2019-05-14_06-55-49.png

Because our FixedUpdate is so fast, after every 100ms frame, the next 5 FixedUpdates execute immediately, before starting on another heavy Update pass. In this situation, the game-time doesn’t slow down, relative to wall-clock time. Instead, game time is shrunken and expanded!

Imagine if you read the wall-time value of Time.realtimeSinceStartup in the FixedUpdate — it would be extremely incorrect. This is a strong reason to keep wall-clock time out of your game calculations. Unity plays around with the way game-time flows depending on the load. You want your game to simulate nicely even when game-time is being stretched.

Conclusion

Let’s take stock:

  • Game-time can drastically differ from wall-clock time, so avoid using the latter.

  • Make use of Time.fixedDeltaTime and Time.maximumDeltaTime to tune the specific performance characteristics of your game. These can all be changed at runtime, too!

  • Stuttering can occur even with an empty load on the engine.

  • Be careful of sampling issues. Debug your frame timesteps to see what is actually happening.

Further, I showed the behavior of the timestep in isolated cases, but in reality all of these situations overlap. A single long Update could trigger a series of longer FixedUpdate steps, causing the Update to slow down, etc. It’s one big feedback loop. Hopefully, though, this gives you some intuition behind it.

Thanks for reading! Drop me a line on Twitter if you have questions or if you have information to add. I’d love to make this article a one-stop-shop for timestep information. Additionally, if you think I’ve gotten something wrong, please let me know!

FAQ

Finally, let’s answer a few frequent questions. Perhaps you’ve come here from Google just for these (I suggest reading the above article for context).

# Help! My game is stuttering!

The first thing to do is figure out why your game is stuttering. Stutter can be a result of mismatched timesteps (see first section) or could be from a variety of different performance issues (your update or fixed update are too long and Unity stutters to compensate in other ways). Drop a few console lines, or graph out the data on when your frames are rendered, and then see what looks wrong compared to the graphs above.

# Is FixedUpdate always called every 0.02 seconds?

No, FixedUpdate will run once for every 0.02 game-time seconds, but precisely when in real time this happens is unspecified and can vary drastically. See above: “A Heavy Load in FixedUpdate”.

# How does the Unity timestep respond to low framerates?

Depending on the source of the slowness, it will do different things. For slow FixedUpdates, it slows down the overall game-time (the game will run for 0.5 seconds in the span of one real time second). In some cases, the rendering rate can be drastically cut, even if the problem is your FixedUpdate speed.

For slow Updates, FixedUpdates get crunched together during the “catch up” phase between the longer Updates. Otherwise, generally the the Update dynamic timestep just gets longer.

# What is the value of Time.deltaTime in the Update function?

You can generally think of it as the difference in game-time between frames being rendered to the screen. When at performance, this is the same as the wall time between frames. However, if the game slows down because of FixedUpdate lag, Time.deltaTime will shrink, even if the render frame rate remains the same! Weird!

# Should I put my code in FixedUpdate or Update?

This is a subtle question, with no correct answer, unfortunately. Code in FixedUpdate means your game is deterministic, but FixedUpdate can’t adapt to varying framerates. Under load, your game will slow down. You must be much more careful that FixedUpdate can run at display frame rate.

If you favor determinism and systematic stability, go for FixedUpdate. If you don’t want to worry about it too terribly much, go with the default Unity approach of putting physics-related code in FixedUpdate and the rest in Update.

I wrote up my opinions in detail in a Reddit comment with two different takes on the matter.

# Does physics have to run in FixedUpdate?

Unity provides the ability to remove the physics update from FixedUpdate, letting you manually step the physics engine on your own time. This is great, but probably should be used with caution. It is a non-standard approach, and may play oddly with plugins and other engine features.

On top of this, Unity now supports the ability to have separate physics worlds. This lets you simulate an entirely separate physics system outside of the game loop.

If you’re reading this from the future, you may also be interested in Unity’s new Havok and DOTS Physics systems.

# Where can I learn more about time steps?

This article is built upon the shoulders of others. Chief among them is the Gaffer on Games: Fix Your Timestep article. See Google for a cached link if it goes down. Additional sources, in no particular order:

Finally, you can reach out to me on Twitter if you have any questions.

John AustinComment