3

In my game-loop, I am using fixed time step for physics and interpolation for rendering as suggested on Gaffer on Games | Fix Your Timestep!

However, when the framerate is varying between 30-60fps during the game, the game looks jumpy. For example, balls suddenly look accelerated when the frame rate increases from 35 to 45 suddenly.

Is there a way to make the game look smooth while framerate is varying?

Here is my game loop:

protected void update(float deltaTime) {
    //do some pre stuff

    deltaTimeAccumulator +=deltaTime; //deltaTimeAccumulator is a class-member holding the accumulated frame time
    while(deltaTimeAccumulator>FIXED_TIME_STEP) {
        world.step(FIXED_TIME_STEP, 6, 2);  //perform physics simulation
        deltaTimeAccumulator-=FIXED_TIME_STEP;
    }
    // world.step(deltaTime, 6, 2);

    destroyBodiesScheduledForRemoval();
    render(deltaTimeAccumulator /FIXED_TIME_STEP); //interpolate according to the remaining time
}

Here is the part related to the interpolation (related inner works of render() method) : this.prevPosition = this.position; //get previously simulated position

    this.position = body.getPosition(); //get currently simulated position

    //interpolate
    Vector2 renderedPosition = new Vector2();
    if (prevPosition != null) {//&& !isFloatApproximatelyEquals(this.prevPosition.x, this.position.x) && !isFloatApproximatelyEquals(this.prevPosition.y, this.position.y)) {
        renderedPosition.x = this.position.x * interpolationAlpha + this.prevPosition.x * (1 - interpolationAlpha);
        renderedPosition.y = this.position.y * interpolationAlpha + this.prevPosition.y * (1 - interpolationAlpha);
    } else {
        renderedPosition = position;
    }

    //Draw the object at renderedPosition
Buddy
  • 131
  • 5
  • 1
    This questions has been answered across many posts on this site. Please search. – Evorlor Aug 15 '15 at 16:01
  • I did search and looked all the topics talking about how to properly make your game independent of the variable time step (or frame time) of the game loop. I implemented my game-loop according to the principle ExOfDe suggested below (similar to what Glenn Fiedler suggested), but i am still getting acceleration for large fps changes. I added my game loop code to be clear. – Buddy Aug 15 '15 at 16:49
  • Are you making sure that you are using the fixed time value everywhere and that no code is using real time? If it isn't that, the problem seems like it is probably in how you render using the remainder of the time step. Can you show us some details on how that code works internally? – Alan Wolfe Aug 15 '15 at 18:48
  • Thank you Alan, i added the inner of the render method showing how remainder of the time step is handled – Buddy Aug 15 '15 at 19:04
  • @EnesBattal Does this jumpiness also occur when not using time remainder interpolation? – akaltar Aug 15 '15 at 20:04
  • @akaltar, yes it does – Buddy Aug 15 '15 at 20:05
  • @EnesBattal Please provide more code, with function headers so we can see where they reside. – akaltar Aug 15 '15 at 20:10
  • 1
    Decrease your maximum delta time (what this code refers to as FIXED_TIME_STEP); what you're describing is temporal aliasing. You need to increase sample frequency in order to minimize it. Interpolation helps with this, but it's quite ineffective in the grander scheme of things. I'm sure you've realized in rendering in general that interpolation doesn't fix a lack of precision. That only works in the movies, where you can "enhance" an image hundreds of times ;) At some point you actually need to increase the sample rate (resolution) of the actual data to get anything meaningful happening. – Andon M. Coleman Aug 15 '15 at 21:50

1 Answers1

6

Temporal Aliasing

As Andon M. Coleman is stating out in the comments what you describe is called temporal aliasing.

Temporal aliasing is due to a too low sampling rate of the scene compared to the the transformation speed of one or more objects within the same scene. In other words if an Object of the scene is more often updated (transformation speed to high) compared to the to the number of frames per second (sampling rate to low) temporal aliasing artefacts are appearing and the Object/s seem/s to jump around.


To avoid temporal aliasing you have to increase the sampling rate aka your FPS without increasing the transformation speed( your number of update calls ).

One way of doing this, see this example here: Vittoria Romeo's Tutorials

            while(running)
            {
                auto timePoint1(chrono::high_resolution_clock::now());

                window.clear(Color::Black);

                inputPhase();
                updatePhase();
                drawPhase();        

                auto timePoint2(chrono::high_resolution_clock::now());
                auto elapsedTime(timePoint2 - timePoint1);
                FrameTime ft{chrono::duration_cast<
                    chrono::duration<float, milli>>(elapsedTime).count()};

                lastFt = ft;

                auto ftSeconds(ft / 1000.f);
                auto fps(1.f / ftSeconds);
            }   

The key is now to take a close look into the updatePhase:

void updatePhase()
        {

        // If `currentSlice` is greater or equal than `ftSlice`,
        // we update our game logic and decrease `currentSlice` by
        // `ftSlice` until `currentSlice` becomes less than `ftSlice`.
        // Basically, if `currentSlice` is three times as big as `ftSlice`,
        // we update our game logic three times.

            currentSlice += lastFt;
            for(; currentSlice >= ftSlice; currentSlice -= ftSlice)
            {   
                //logic
                someupdatefunction(FixedtimeStep)
            }
        }

What we do is to divide our time in different slices:

 |............|............|............|..........
 |            |            |            |
 \-> slice    \-> slice    \-> slice    \-> slice

And, every frame, we will call update(mFT) "n" times, where "n" is the number of slices we passed through with our last frametime. Example:

       v-----+frametime---+--v
 |............|............|............|..........
 |            |            |            |
 \-> slice    \-> slice    \-> slice    \-> slice

In the above example, in the time it took to process a single frame, we went over two different slices. We will then call update(mFT) twice in the same frame.

No matter how big or how small or last frametime was, we will always call update(mFT) in fixed intervals (our slices). If the game goes extremely fast, we will completely skip the updating phase during certain frames. If the game goes extremely slow, we will probably need to update multiple times during a single frame. This method allows fine control over our movement precision, and guarantees the same behavior with any FPS.

This explanation is from Vittorio Romeo. You will find a complete tutorial how to implement this there via source code or Video. Vittoria Romeo's Tutorial

ExOfDe
  • 213
  • 1
  • 8
  • I am doing exactly what you suggested, it is similar to what Glenn Fiedler suggested but i am still getting jumpy results for large fps changes. – Buddy Aug 15 '15 at 16:48
  • can you maybe show us the relevant code it could help to solve your problem – ExOfDe Aug 15 '15 at 16:50
  • I have not tested it but i assume the difference i do not see this behaviour with my loop is because i define the "slice" size for my inner logic loop and i compare my "accumulator" (currentslice) not with the fixed timestep like you did but instead with the desired slice size.

    This is the key.

    – ExOfDe Aug 15 '15 at 17:05
  • slice size should be equivalent to fixed time step in my implementation – Buddy Aug 15 '15 at 17:10
  • any specific reason for this implementation? – ExOfDe Aug 15 '15 at 19:37
  • 1
    @EnesBattal: If that's necessary, then your only option is to increase the sample frequency to reduce temporal aliasing. Interpolation never fixes aliasing problems, it only hides them. To increase sample frequency, cut your FIXED_TIME_STEP value down. That's effectively what the snippet in this answer does, only it attempts to solve this problem dynamically instead of trying to pick one value that's supposed to work across all framerates. I like ExOfDe's solution, personally. Tuning FIXED_TIME_STEP using hand-picked constant values is laborious and not very flexible. – Andon M. Coleman Aug 16 '15 at 12:31
  • It's of course no longer a fixed timestep, but that's not necessarily a bad thing if your framerate is crazy variable and you don't want to set a constant timestep very low. – Andon M. Coleman Aug 16 '15 at 12:35
  • The example from the tutorial on github nor the youtube video doesn't explain what "milli" is. Im assuming a typedef of "std::chrono::milliseconds," but when I run it, I get c2328 'duration cannot have duration as first template object' and C2440 "initializing' cannot convert from std::chrono::milliseconds to float – ChocoMan Jul 14 '16 at 07:32
  • 1
    Nevermind. My rubber duckie found it :) – ChocoMan Jul 14 '16 at 07:43