3
\$\begingroup\$

I'm messing around with libgdx and box2d and I'm trying to implement sidescroller movement with fixed timestep logic according to the famous Fix Your Timestep! tutorial/guide. However, logic updating seems to be twitchy and I'm not exactly sure why.

Here's the render method where the issue lies:

public void render() {
    handleCameraInput();
    handleTestBodyInputImpulse();
  //handleTestBodyInputVelocity();

    accumulator += Math.min(Gdx.graphics.getDeltaTime(), STEP);

    int logicStepCount = 0;
    while (accumulator >= STEP) {
        world.step(STEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS);
        accumulator -= STEP;
        ++logicStepCount;
    }

    Gdx.graphics.setTitle("FPS: " + Gdx.graphics.getFramesPerSecond() + ", Logic steps: " + logicStepCount + ", Test body vel: " + testBody.getLinearVelocity());

    cam.update(); // update box2d world camera
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    renderer.render(world, cam.combined); // renders box2d bodies
}

From what I've noticed the logic while loop responsible for updating the physics sometimes doesn't execute (counter goes from 1 to 0 for one frame) and that seems to be causing the twitching. This is weird to me since delta time added to the accumulator is clamped and since the FPS is constant 60 on my machine I'd assume that the loop should always execute once. Am I doing something wrong? Is this behavior normal and is it something I am supposed to solve during rendering with interpolation?

Complete minimal example can be found here on Gist and can be run on any LibGdx project with Box2d dependency.

\$\endgroup\$
1
\$\begingroup\$

For one, your FPS is not constant; it is being truncated/rounded into an int.
It is also probably being averaged over a number of frames.

Most likely, every Xth frame is taking only 16ms to complete (62.5 FPS), instead of 16.66666666666ms (60.0 FPS). Since (16.0 < 16.66666), no physics update takes place. With an unlocked time-step, this occurs often (adding smaller fractions, more often) which is the reason you are implementing a "semi"-fixed time-step.

So, you want 600 to be 10 * 60, not 20-5 * 30-120. It's normal that it varies slightly; the accumulator's job is to make sure that you only spend time doing updates that are "worth it".

Likewise, your computer has other things to do than run your game; frames may occasionally take 17ms (58.8 FPS) to complete. By clamping deltaTime, you have thrown away 1ms of real time. If that happens every frame at "60 FPS", that equates to "lost time" of approximately 60ms per second (6%).

Without excessive drag on your CPU:

  • If STEP is equal to your expected deltaTime, you'll usually run one update per frame, but occasionally zero or two. This is a frequently desired behavior.
  • If STEP is less than your expected deltaTime, you'll always run one update per frame, but may frequently run two (or more). This is desirable if you want to use higher precision for physics updates than graphical updates. If you are careless, this can lead to the "spiral-of-death" and is one symptom of the orginal, "un-fixed", time-step where STEP is effectively 0 and the CPU can't catch up.
  • If STEP is greater than your expected deltaTime, you'll spend proportionally more frames accumulating than you do updating. Without optimizing, this method renders many identical frames which is a second symptom of the original, "un-fixed", time-step, where the CPU and/or GPU are forced to work over-time, even when the game is idle.

Newcomers having time-step problems often fail to understand the difference between graphical-FPS and logic-FPS because they are both frequently referred to as "FPS". So, they "fix" problems within Update(), by calling Render() more often, trying to use all available power to spread the error out thin enough that it won't be noticeable. The problem is that the game produces different results on different computers; many of which are not desirable. That, of course, leads to the "Fix Your Timestep" article.

\$\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.