How to make your game run at 60fps

What if they forced vsync off in their driver settings?So you think, well I gotta measure time in here somewhere and make sure I’m updating at the right frequency.

It’s fairly simple to do, you just accumulate time every loop and do an update every time it goes over the threshold of 1/60 of a second.

while(running) { deltaTime = CurrentTime()-OldTime; oldTime = CurrentTime(); accumulator += deltaTime; while(accumulator > 1.

0/60.

0){ update(); accumulator -= 1.

0/60.

0; } render(); display();}Done.

Boom.

Easy.

In fact there’s a ton of games out there that ship with code that looks basically exactly like this.

But it’s wrong.

This works fine for regulating timing but introduces stuttering issues and other kinds of inconsistencies.

A common one here is just that frames are not exactly 1/60th of a second even if you have vsync on, there’s a bit of noise in how long they take (and how precise the OS timer is).

So you’d get situations where you render a frame, and the game doesn’t think it’s time to update again (because the accumulator is behind by a tiny minuscule amount) so you just repeat the same frame again, but now the game is a frame behind so it does a double update.

Stutter!So there’s a few existing solutions to fixing that stutter you can find with some google searching, for instance you could have your game use a variable timestep instead of a fixed timestep and just skip the accumulator junk in your timing code entirely.

Or you can do a fixed timestep with an interpolated renderer, as described in the pretty famous “Fix Your Timestep” blog post from Glenn Fielder.

Or you can fudge your timer code to be a little bit more lenient, as described in the “Frame Timing Issues” blog post from Slick Entertainment (unfortunately the blog no longer exists).

Fuzzy TimingSlick Entertainment’s method of “timing fuzziness” was the easiest to implement in my engine, as it didn’t require any changes within game logic or rendering, so I did that for The End is Nigh.

It was about as plug and play as it gets.

In summary, it basically just lets the game update “a little bit early”, so as to avoid timing inconsistency issues.

If the game is vsynced this should let it just use the vsync as the main timer for the game, and you’d get a buttery smooth experience.

Basically, this is what the code for updating looks like now (the game “can run” at 62 fps, but it still treats each timestep as if it was 60fps.

I’m not sure why it needs to clamp it to prevent the accumulator from going below 0, but it doesn’t work without that).

You can interpret this as “the game updates in lockstep if its rendering between 60fps and 62fps”:while(accumulator > 1.

0/62.

0){ update(); accumulator -= 1.

0/60.

0; if(accumulator < 0) accumulator = 0;}If you’re vsynced, this basically just lets the game be in lock step with the monitor’s refresh rate, and you get a buttery smooth experience.

The main issue here is you would run *slightly* fast if you were not vsynced, but it’s such a minor difference that nobody would notice.

Speedrunners.

Speedrunners noticed.

Shortly after the game was released they noticed that some people on the speedrun records list had worse in-game-times but slightly better measured times than others.

And this was directly caused by the timing fuzziness and something forcing vsync off in the game (or running on a 144hz monitor).

So it was clear I needed to disable that fuzziness if vsync was off.

Oh but there’s no way to check if vsync is off.

There’s no OS call for it, and while you can request for vsync to be enabled or disabled from your application, it’s completely up to the OS and graphics driver on whether or not to actually enable it.

The only thing you can do is render a bunch of frames and try to measure how long they take, and try to see if they all take about the same time.

So that’s what I did for The End is Nigh.

If it wasn’t vsynced at 60hz, it falls back to the original “strict 60 fps” frame timer.

Plus I added a config file setting to force it to not use fuzziness (mainly there for speedrunners who want accurate times), and gave them an accurate in-game timer hook they could use for their autosplitter.

Some people still complained about occasional single frame stutters, but they seemed rare enough that they were probably just OS events or something.

Not a big deal.

Right?Recently when reviewing my timer code I noticed something odd.

The accumulator was drifting, every frame would take a little bit longer than 1/60th of a second, so periodically the game would think its a frame behind and do a double update.

It turns out my current monitor is 59.

94hz instead of 60hz.

This meant that once every 1000 frames, it would need to do a double update to “catch up”.

Simpleish fix though, instead of having the range of acceptable framerates be 60 to 62, you just make it 59 to 61 instead.

while(accumulator > 1.

0/61.

0){ update(); accumulator -= 1.

0/59.

0; if(accumulator < 0) accumulator = 0;}The previously described issue about disabled vsync and high refresh rate monitors is still there, and the same solution still applies (fall back to the strict timer if the monitor is *not* vsynced at 60).

But how do I know this is an appropriate solution?.How can I test this to make sure it works properly on all combinations of computers with different kinds of monitors, vsync on and vsync off, etc?.It’s really hard to track this timer stuff in your head and figure out what causes desyncs and weird cycles and stuff.

The Monitor SimulatorWhile trying to figure out a robust solution for the “59.

94hz monitor problem” I realized I can’t just trial and error this on my computer and expect it to be a robust solution.

I needed a good way to test various attempts at writing a better timer and an easy way to see if they would cause stuttering or time drift on various monitor configurations.

Enter the Monitor Simulator.

It’s a quick and dirty piece of code I wrote that simulates “how a monitor works” and basically prints out a bunch of numbers that tell me how stable whatever timer I’m testing is.

The original naive stuttery frame timer prints out this, for instance20211012021011202111020211102012012102012[.

]TOTAL UPDATES: 10001TOTAL VSYNCS: 10002TOTAL DOUBLE UPDATES: 2535TOTAL SKIPPED RENDERS: 0GAME TIME: 166.

683SYSTEM TIME: 166.

7It first prints a number each simulated vsync of how many times the game loop “updated” since the last vsync.

Anything other than a bunch of 1s in a row is a stuttery experience.

At the end it prints some collected statistics.

Using the “fuzzy timer” (with a range of 60–62fps) on a 59.

94hz monitor, it prints out this111111111111111111111111111111111111111111111[.

]TOTAL UPDATES: 10000TOTAL VSYNCS: 9991TOTAL DOUBLE UPDATES: 10TOTAL SKIPPED RENDERS: 0GAME TIME: 166.

667SYSTEM TIME: 166.

683It takes a while to get a frame stutter, so it can be hard to notice where that happens in the mass of 1s.

But the stats it prints clearly shows that it had a few double updates in there, and thus would be a stuttery experience.

The fixed version (with a range of 59–61 fps) has 0 skipped or doubled updates.

I can also disable vsync.

The rest of the output is irrelevant, but it can clearly show me how much “Time Drift” occurred (system time is off from where game time should be).

GAME TIME: 166.

667SYSTEM TIME: 169.

102This is why you need to switch back to the stricter timer if vsync is off.

That discrepancy adds up over time.

If I set render time to .

02 (so it takes “more than a frame” to render), I get stuttering again.

Ideally this should make the game’s frame pattern be 202020202020, but it’s slightly uneven.

This timer does slightly better in that situation than the previous one, but its getting more and more complicated and harder to see how or why it works.

But hey I can just shove tests at this simulator and see how they do, and then try to figure out why they work later.

Trial and error baby!while(accumulator >= 1.

0/61.

0){ simulate_update(); accumulator -= 1.

0/60.

0; if(accumulator < 1.

0/59.

0–1.

0/60.

0) accumulator = 0;}Feel free to download the monitor simulator yourself and try various timing methods.

Absolutely tweet at me if you find anything better.

I’m not 100% happy with my solution (it still requires that “detect vsync” hack, and it can still do a single stutter if it ever gets out of sync), but I think this is about as good as you’re going to get for trying to do a lockstep game loop.

Part of the problem is its just really difficult to determine the parameters of what counts as “acceptable” here.

It’s all about the tradeoff between time drift and doubled/skipped frames.

If you shove a 60hz game on a 50hz PAL monitor… what even is the correct solution here?.Do you stutter like crazy or do you run noticeably slower?.Both options just feel bad.

Decoupled RenderingThe previous methods I’ve described are what I refer to as “lockstep rendering”.

You update, then render, and whenever you render you’re always showing the most recently computed game state.

Rendering and updating are coupled together.

But you can decouple them.

That’s what the method in the Fix Your Timestep post described.

I am not going to reiterate what’s in that post, so you should definitely give it a read.

This is (as far as I can tell) the “industry standard” method used in AAA games and engines like unity or unreal (Tight action-oriented 2D games usually prefer lockstep though, because sometimes you just need the precision you get from that method).

In summary though, that post just describes the method where you update at a fixed framerate, but when you render you interpolate between the “current” game state and the “previous” game state using the current accumulator value as the measure of how much to interpolate by.

This way you can render at whatever framerate you want, and update at whatever update rate you want, and it will always be smooth.

No stutters, works universally.

while(running){ computeDeltaTimeSomehow(); accumulator += deltaTime; while(accumulator >= 1.

0/60.

0){ previous_state = current_state; current_state = update(); accumulator -= 1.

0/60.

0; } render_interpolated_somehow(previous_state, current_state, accumulator/(1.

0/60.

0)); display();}Boom.

Easy.

Problem solved.

Now to just get it so my game can render interpolated game states and… wait that’s actually not simple at all.

This post just assumes that’s a thing you can do.

Its easy enough to cache the previous transform of your game object and interpolate transforms, but games have a lot more state than just that.

There’s animation states and object creation and destruction and a lot of other shit to take into consideration.

Plus in game logic you now have to care whether or not you’re teleporting an object or smoothly moving it to avoid the interpolator making wrong assumptions about the path a game object took to get where it is.

Rotations can be a mess especially if you’re changing a rotation by more than 180 degrees in a single frame.

How do you correctly handle objects being created or destroyed?I’m currently working on this in my own engine, and basically just interpolate transforms and let everything else remain as it was before.

You don’t really notice stuttering if something isn’t smoothly moving, so animations skipping frames and object creation/destruction being up to a frame off sync isn’t an issue if everything else is smooth.

It is weird however that this method basically has the game render up to 1 game state behind where the simulation currently is.

It’s not really noticeable but it can compound with other sources of delay like input lag and monitor refresh rate, and anyone who wants the most responsive game experience (hey speedrunners) would probably much rather have the game be lockstep instead.

In my engine I’m just making this be an option.

If you have a 60hz monitor and a fast computer, use lockstep with vsync on for the best experience.

If you have a monitor with a weirder refresh rate, or a weaker computer that cant consistently render at 60, then turn on frame interpolation.

I want to call this “unlock framerate” but am worried people think that just means “turn this on if you got a good computer”.

That’s a problem to solve later though.

Now there *is* a method that sidesteps that problem though.

Variable Timestep UpdatesI got a bunch of people asking why not just update with a variable timestep, and often see armchair programmers say “well if a game is programmed CORRECTLY they just update at arbitrary timesteps”.

while(running) { deltaTime = CurrentTime()-OldTime; oldTime = CurrentTime(); update(deltaTime); render(); display();}No weird timing junk.

No weird interpolated rendering.

It’s simple and it works.

Boom.

Easy.

Problem solved.

For good this time!.Can’t get any better than this!Now you just need to make your game logic work on arbitrary timesteps.

Easy right, you just go through and change code that looks like this:position += speed;to this:position += speed * deltaTime;and you change code that looks like this:speed += acceleration;position += speed;to this:speed += acceleration * deltaTime;position += speed * deltaTime;and you change code that looks like this:speed += acceleration;speed *= friction;position += speed;to this:Vec3D p0 = position;Vec3D v0 = velocity;Vec3D a = acceleration*(1.

0/60.

0);double f = friction;double n = dt*60;double fN = pow(friction, n);position = p0 + ((f*(a*(f*fN-f*(n+1)+n)+(f-1)*v0*(fN-1)))/((f-1)*(f-1)))*(1.

0/60.

0);velocity = v0*fN+a*(f*(fN-1)/(f-1));….

wait hold upwhere the fuck did that come from?Ok that last bit is literally cut and pasted from my engine utility code for “actual correct framerate independent move with speed-limiting friction” function and contains a little bit of extra cruft in there (those multiplies and divides by 60).

But that is the “correct” variable timestep version of the previous snippit.

I calculated it over the course of an hour or so with gratuitous help from wolfram alpha.

Now there’s going to be people saying why not just do:speed += acceleration * deltaTime;speed *= pow(friction, deltaTime);position += speed * deltaTime;And while something like that kinda works, it’s not actually correct.

You can test it yourself.

Do 2 updates of that with deltaTime set to 1, and do it once with deltaTime set to 2, and the results aren’t actually the same.

Typically you want your game to run consistently, so having inconsistencies like this aren’t great.

Its probably good enough if you know your deltaTimes are all around the same value, so then you need some code to make sure your updates are running at some kind of fixed rate and… oh.

Right.

We’re trying to do it the “CORRECT” way now.

If that tiny bit of code expands to that monstrous pile of math, imagine more complicated movement patterns involving multiple interacting objects and such.

You can clearly see how doing it the “correct” way is infeasible.

So the “rough approximation” is basically all you got.

Lets ignore that for now and assume you actually do have the “actual correct” version of your movement functions.

Good, right?Well, no.

Here’s an actual real life example of an issue I had with this in Bombernauts.

You can jump about 1 tile high, and the game takes place on a grid of 1 tile blocks.

Your feet need to clear the top of the block in order to land on it.

But since collision detection here is in discreet steps, if the game was running at a slower framerate your feet would sometimes not actually clear the top of the tile, even though the movement curve they followed was the same, and you would just slide down the wall instead.

This is obviously a solvable problem.

But it illustrates the types of problems you encounter when trying to make your variable timestep game loop work correctly.

You lose consistency and determinism, so you can just throw away the ability to do input replays or deterministic multiplayer and such.

For a 2D action reflexy game, consistency matters a ton (hey speedrunners).

If you’re trying to regulate your timesteps so they aren’t too large or too small then you kinda lose the main benefit you get from doing variable timestep in the first place, and you may as well just use one of the other 2 methods I described here instead.

It’s not worth it.

There’s too much extra effort involved on the game logic side of things (making sure your movement math is correct) and it requires too many sacrifices in the determinism and consistency department.

I would only use this method for something like a rhythm game (where movement equations are simple and you want the maximum responsiveness and smoothness possible).

Otherwise gimme that fixed update.

ConclusionYou now know how to make your game run at a consistent 60fps.

It’s trivially easy and there’s no reason anyone should have ever had any trouble with it before.

There’s no other issues that could complicate this further.

Thanks for reading you can follow me on twitter for more hot gamedev tips.

.. More details

Leave a Reply