Game Loop Answers

1. When Vsync is enabled the game will be kept at a frame rate consistent with your monitor's refresh rate. In most cases this means that it will automatically be capped at 60fps. To test that this is the case, we can create a variable named frame which will count the frame we're in. We start it at 0 and increase it by 1 every frame:

 

And if you run this you should see the number printed go up by about 60 every second.

However, if you turn Vsync off by calling love.window.setMode(800, 600, {vsync = false}) in love.load and then run it again, you'll see that it goes up much faster. This is because by default the love.run function contains almost no code to keep the while true loop from going super sonic fast, which means that it will do just that.

The only thing that does keep it in check somewhat is the love.timer.sleep(0.001) call, which is explained in the love.run page. As explained there, if Vsync is disabled, this call is used to keep the FPS capped at 1000, to decrease CPU usage and give the OS control for a bit every frame.

For the next exercises we'll assume that Vsync is enabled. The Fix Your Timestep article makes the same assumption, but also does a good job of outlining the problems with each method and also considering what happens with each when Vsync might be turned off.


2. We want to implement the Fixed Delta Time loop. As the article states, this one uses a fixed delta of 1/60s. In the default love.run function the delta changes based on the value returned by love.timer.getDelta, and that changes based on the time taken between the two last frames. So the default implementation is definitely not a fixed delta of 1/60s. To achieve that, we need to first remove the sections that change the delta in any way. So this piece of code will be removed:

 

And then we need to define our dt variable to the value we want it to have, which is 1/60:

 

And so the love.run function would look like this:

 

The problems with this approach are explained in the article, but that's how you'd do it if you wanted to for some reason.


3. Now we want to implement the Variable Delta Time loop. The way this is described is as follows:

Just measure how long the previous frame takes, then feed that value back in as the delta time for the next frame.

If we go back to the description of the love.timer.step and love.timer.getDelta functions that are used in the default love.run implementation, it turns out that that's exactly what they do. So there seems to be no differences between the default love.run implementation and the Variable Delta Time loop. This is what it looks like:

 

The problems with this approach are also well explained in the article.


4. Now for the Semi-fixed Timestep loop. We should now add an upper-bound for the dt value as well as making sure that if that value goes above that limit, we divide the timestep into multiple parts. We can achieve that by simply changing what happens around the love.update call:

 

The dt variable corresponds to frameTime and current_dt corresponds to deltaTime. We also use upper_dt, which can be defined where dt is initialized like local upper_dt = 1/60. And all that looks like this:

 


5. Finally, the Free the Physics loop, which is similar to the previous solution, except that we constrain ourselves to only go with fixed steps. The benefits that come from this is that our simulations are going to be more well behaved and predictable even under various different circumstances.

The only thing we really need to do is setup everything that happens around the love.update call as the pseudocode from the article shows:

 

And so this follows the same logic as the previous exercise, where if the frame time exceeds a certain value, then we subdivide the timestep into multiple parts (but this time they're parts of fixed size). The accumulator and fixed_dt variables can be set up outside the while loop and it all looks like this: