1
\$\begingroup\$

My goal:

  • Implement the game update loop so that it's deterministic (i.e. every frame of updation behaves the same when run multiple times with same frame-input, does not depend on processing speed etc.)
  • Limit the framerate of the game at (maximum) 60fps (can be slower if CPU hangs)

What I don't want to do:

  • I don't want to use a delta_time to compute position updates, because it violates the goal
  • Don't want to introduce any additional input-lag that would come with any sort of buffering (for interpolating time between frames etc.)

What I came up with:

  • I use vsync to guarantee that the game never produces more than 60 fps
  • I disallow any screen-mode that is not running at 60Hz because the game would run at different speeds
  • I allow windowed mode only if the current screen-mode is at 60Hz

Is there a better approach that what I'm doing to achieve what I want, perhaps without limiting the supported refresh rates?

\$\endgroup\$
  • 2
    \$\begingroup\$ The usual advice here is to use a fixed timestep, so your delta time is consistent for every simulation step, regardless of the rendering framerate. Have you tried this? \$\endgroup\$ – DMGregory Sep 22 at 11:47
  • \$\begingroup\$ So basically that is what my solution is doing, together with vsync and forcing the 60Hz which guarantees that the game will run at the same (max) speed every time. This still won't allow me to support many refresh rates though, hence my question. I suppose there just isn't a good way to target all refresh rates while keeping the update deterministic across platforms.... \$\endgroup\$ – Lake Sep 22 at 12:03
  • 2
    \$\begingroup\$ It sounds like you've misread the article. Supporting a variety of rendering/refresh rates while keeping the simulation tick consistent is exactly what a fixed timestep accomplishes. I explain this at more length here. Your simulation step does not have to be equal to or related in any way to your rendering interval. \$\endgroup\$ – DMGregory Sep 22 at 12:06
  • 1
    \$\begingroup\$ The input delay is actually less than one frame when you're using interpolation, because the target point of the interpolation is moved by the latest input, so you can see a change immediately in the interpolated result, without waiting for that simulation step to move all the way from future to past. You can also apply purely visual/auditory responses to the input instantly in the frame it's received. So you can keep instantaneous response while also getting simulation consistency this way. \$\endgroup\$ – DMGregory Sep 22 at 12:19
  • 1
    \$\begingroup\$ No, that is not normally how it's done. Since the rendering step needs to wait until the newest game state update is completely finished before interpolating and rendering it, it's not quite as simple as running these two features in parallel. Even single-treaded solutions can get a lot of these benefits, as shown in the Gaffer on Games examples. \$\endgroup\$ – DMGregory Sep 22 at 12:30
2
\$\begingroup\$

DMGregory guided me towards a good solution explained in this article:

Use a fixed timestep

This solution allows the total decoupling of the simulation and rendering frequency, while allowing for a fixed timestep for the simulation part.

Visual smoothness is preserved by interpolating between the previous and current (last) state of simulation with a blending factor that is given by the total remainder amount of non-yet-simulated time (since simulation happens in fixed step intervals).

This introduces a <1 frame lag between the rendering and the simulation as the rendered state lags behind the simulation, but this does not introduce input lag as the inputs are taken into account in the blending through the current state.

So in terms of lag, responsiveness remains instantaneous while the actual drawn position falls behind by a fraction of an update.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.