How to make your game run at 60fps

while(running) {
update();
render();
display();
}
while(running) {
deltaTime = CurrentTime()-OldTime;
oldTime = CurrentTime();
accumulator += deltaTime;
while(accumulator > 1.0/60.0){
update();
accumulator -= 1.0/60.0;
}
render();
display();
}

Fuzzy Timing

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

while(accumulator > 1.0/62.0){
update();
accumulator -= 1.0/60.0;
if(accumulator < 0) accumulator = 0;
}
while(accumulator > 1.0/61.0){
update();
accumulator -= 1.0/59.0;
if(accumulator < 0) accumulator = 0;
}

The Monitor Simulator

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

20211012021011202111020211102012012102012[...]TOTAL UPDATES: 10001
TOTAL VSYNCS: 10002
TOTAL DOUBLE UPDATES: 2535
TOTAL SKIPPED RENDERS: 0
GAME TIME: 166.683
SYSTEM TIME: 166.7
111111111111111111111111111111111111111111111[...]TOTAL UPDATES: 10000
TOTAL VSYNCS: 9991
TOTAL DOUBLE UPDATES: 10
TOTAL SKIPPED RENDERS: 0
GAME TIME: 166.667
SYSTEM TIME: 166.683
GAME TIME: 166.667
SYSTEM TIME: 169.102
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;
}

Time Snapping

After posting this article originally I came up with another way to do a fixed timer that avoids the pitfalls of needing to know whether vsync is enabled or not, and is fairly robust and doesn’t care about sync issues like the previous method did.

if(abs(delta_frame_time - 1.0/120.0) < .0002){
delta_frame_time = 1.0/120.0;
}
if(abs(delta_frame_time - 1.0/60.0) < .0002){
delta_frame_time = 1.0/60.0;
}
if(abs(delta_frame_time - 1.0/30.0) < .0002){
delta_frame_time = 1.0/30.0;
}
accumulator += delta_frame_time;while(accumulator >= 1.0 / 60.0){
update();
accumulator -= 1.0 / 60.0;
}

Decoupled Rendering

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

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();
}

Variable Timestep Updates

I 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();
}
position += speed;
position += speed * deltaTime;
speed += acceleration;
position += speed;
speed += acceleration * deltaTime;
position += speed * deltaTime;
speed += acceleration;
speed *= friction;
position += speed;
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));
speed += acceleration * deltaTime;
speed *= pow(friction, deltaTime);
position += speed * deltaTime;

Conclusion

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

Addendums

I’m thrilled at the reception this blog post originally received, and I want to make sure this is the most up to date and thorough resource for this common gamedev problem out there. So I will update this article as new information is brought to my attention, and I’m also adding a few more pieces of information here that I couldn’t find the place for in the original article.

Hybrid Approaches

Unity (and other big engines) use a hybrid approach. Unity provides Update() and FixedUpdate() callbacks separately. Update uses variable time steps and FixedUpdate uses fixed time steps, plus it automatically interpolates stuff like physics states and animation states. If you mix and match both of those update callbacks without knowing how they work under the hood, you end up getting weird stuttering inconsistencies in your unity project. It’s a common problem I’ve seen in unity games, so even if you are using an engine, you still should understand how this all works.

1000hz Fixed Update

I’ve seen a few people mention to me their solution to this is to just update at a fixed rate of 1000 times per second. Because the difference between doing 1 and 2 updates per frame is a lot more noticeable than the difference between 16 and 17 updates a frame. You can do this if your game is pretty simple, but it does not scale well to more complicated projects.

Timing Anomalies

You do need to account for various timing anomalies when measuring frame code. If delta time is less than 0, that means that the system timer wrapped around. If it’s really high, you probably don’t want to fast forward your game a ton in one step, so you probably should cap it. If you just clamp deltaTime to between 0 and <maximum delta time> (this is 8/60 (7.5fps) for me), that should account for most anomalies.

Resyncing

In my engine I have a manual callback I can use to “resync” my timer code (set the accumulator to 0 and delta time to 1/60 the next time through the loop), which I do after loading a level or swapping scenes. You need this because you typically don’t want the game to start each level by immediately trying to make up the time it spent loading.

Spiral of Doom

If your game cannot *update* at 60hz, you end up in a spiral of doom where your game can never catch up to where it should be, and so it will do more and more updates every time until it eventually just freezes. Cap your accumulator to a maximum (I use 8/60 (7.5fps) as the max) and it should prevent that issue. This will not be a fun experience for whoever is trying to play the game, but at least it won’t freeze.

Floating Point Inaccuracies

In this article and my monitor simulator code, I was using doubles for clarity. Using doubles or floats introduces floating point error, like adding 1.0/60.0 60 times in a row will not actually end up being exactly 1. In my engine I actually use 64 bit integers for my timer code instead to sidestep this. SDL reports the system timer as a 64 bit int, so I just keep it in this format to avoid the loss you get from converting it to a double. In the game itself, this gets converted to double, but the timer code keeps it as an int.

Situations where interpolation is not an option

Interpolation is the industry standard method of doing this, because it’s a good robust solution that works great in the majority of use cases. But this has resulted in a few people acting as if there is no reason to even consider lockstep, and existing resources about this basically never even discuss it. Ignoring the extra difficulty involved in getting interpolation working, there are a few situations where interpolation just inherently isn’t an option, like emulators. It’s up to you to determine what your needs and options are here for your specific use case.

Averaging Delta Time

Some games and engines average together the previous few time deltas to smooth out any single frame spikes. For example, instead of a single slow frame resulting in 4 updates on the next frame, it would do 2 updates for the next 3 frames instead. You do this averaging when you compute delta time, before adding to the accumulator.

Update Multiplicity

An uneven framerate is worse than a slow framerate. For this reason I have a setting in my engine called “update multiplicity” that basically just makes the game always do a multiple of N updates at a time.

while(accumulator >= (1.0 / 60.0) * update_multiplicity){
for(int i = 0; i<update_multiplicity; i++){
simulate_update();
accumulator -= 1.0 / 60.0;
}
}

Sample Code

This is production code from my engine that I have commented. It uses many of the techniques and tricks I’ve illustrated in this post. You probably can’t just plug and play this into your engine, but I’ve decided to put this up for reference because sometimes you just wanna see what everything looks like together.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store