Game loop with interpolation - weird step back

547 views Asked by At

I have read about an interpolation applied to game loops and tried to implement it myself. It looks almost same as I expected, but when the object ends its movement weird step back takes place. I decided to paste here full source, because this problem may be caused by everything.

#include <SFML/Graphics.hpp>
#include <chrono>

sf::RenderWindow window(sf::VideoMode(800, 600), "Interpolation");
sf::Event event;
int fps = 10; // set to 10 for testing purpose
std::chrono::nanoseconds timePerFrame = std::chrono::seconds(1);
std::chrono::nanoseconds accumulator;
std::chrono::steady_clock::time_point start;
sf::RectangleShape shape1(sf::Vector2f(50, 50));
sf::RectangleShape shape2(sf::Vector2f(50, 50));
sf::Vector2f movement(0, 0);
sf::Vector2f position1(375, 100);
sf::Vector2f position2(375, 275);

void initialization();
void processInput();
void update();
void interpolate();
void render();

int main()
{
    initialization();
    while(window.isOpen())
    {
        start = std::chrono::steady_clock::now();
        processInput();
        while(accumulator >= timePerFrame)
        {
            update();
            accumulator -= timePerFrame;
        }
        interpolate();
        render();
        accumulator += std::chrono::steady_clock::now() - start;
    }
    return 0;
}

void initialization()
{
    timePerFrame /= fps;
    shape1.setPosition(position1);
    shape2.setPosition(position2);
}

void processInput()
{
    while(window.pollEvent(event))
    {
        if(event.type == sf::Event::Closed) window.close();
    }
}

void update()
{
    if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) movement = sf::Vector2f(-300, 0);
    else if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) movement = sf::Vector2f(300, 0);
    else movement = sf::Vector2f(0, 0);
    position1.x += movement.x / fps;
    position2.x += movement.x / fps;
    shape1.setPosition(position1);
    shape2.setPosition(position2);
}

void interpolate()
{
    double interpolationFactor = (double) accumulator.count() / timePerFrame.count();
    shape2.setPosition(position2.x + (movement.x / fps * interpolationFactor), position2.y);
}

void render()
{
    window.clear(sf::Color::Black);
    window.draw(shape1);
    window.draw(shape2);
    window.display();
}

I do not know what may cause that kind of problem. I'm looking forward your help.

1

There are 1 answers

3
1201ProgramAlarm On

Your interpolate function can count some parts of the time interval multiple times.

Since accumulator only resets every timePerFrame ticks, a fast loop rate can add smaller intervals multiple times. If the main loop runs in 0.01 seconds, the first call to interpolate uses that 0.01 in the interpolation factor. The next time, it uses 0.02 (for a total add of 0.03). This continues until there is enough time accumulated for update to update the position using 0.1 seconds (the time step). Since interpolate added in more time than that, the object jumps back.

interpolate should only add in the time of the current step, and not the fully accumulated time. (Also, rather than calling now to get the start time every loop, the previous loop's now value used for the end time should be used as the start time for the next loop. Otherwise you'll lose occasional clock ticks when it changes between the end of one loop and the start of the next.)