Search code examples
libgdxphysics

libgdx - fixed timestep with interpolation - without box2d


I am having some problems implementing fixed timestep with graphic interpolation in my game.

Here is part of the render method:

@Override
public void render(float delta)
{
    double newTime = TimeUtils.millis() / 1000.0;
    double frameTime = Math.min(newTime - currentTime, 0.25);

    accumulator += frameTime;
    currentTime = newTime;

    while (accumulator >= step)
    {
        updateObjects(step);
        accumulator -= step;
    }

    double alpha = accumulator / step;

    interpolateObjects((float)alpha);
}

Here is updateObjects:

for (int i = 0; i < world.level.gameObjects.size(); i++)
{
    GameObject go = world.level.gameObjects.get(i);
    go.prevPosition.set(go.position);//save previous position
    go.update(delta);
}

interpolateObjects:

for (int i = 0; i < world.level.gameObjects.size(); i++)
{
    GameObject go = world.level.gameObjects.get(i);
    go.position.lerp(go.prevPosition, alpha);
}

And then objects are rendered using position

As far as i can tell this should work, but it doesn't. On high fps (200-400) everything is too slow, movement isn't even visible, i can just see that position is changing by 0.0001 or something like that

On low fps (10-20), movement is visible but again objects are very slow...

If i disable interpolation, than everything works as it should (on any fps), but then everything is jittery.

So the problem is somewhere in interpolation.


Solution

  • Your interpolation go.position.lerp(go.prevPosition, alpha) is set up to assume that prevPosition was last updated at an exact multiple of step, but then when you update prevPosition like this go.prevPosition.set(go.position) you are destroying that contract on the first update of the frame. It also looks like you are lerping backwards (from position to the previous position).

    I think you need a third vector so the last interpolated value is guaranteed not to influence your fixed time updates. Here I'll call it interpPosition, and it will be used for drawing instead of position.

    You actually seem to technically be extrapolating (not interpolating) the value, since you are not updating ahead of time and your alpha is calculated from time left in the accumulator. If you want to linearly extrapolate from the last two positions calculated, you can do it like this (note the 1+alpha to extrapolate):

    for (int i = 0; i < world.level.gameObjects.size(); i++)
    {
        GameObject go = world.level.gameObjects.get(i);
        interpPosition.set(go.prevPosition).lerp(go.position, 1 + alpha);
    }
    

    Depending on the speed of your simulation (and how fast objects can accelerate), this might still look jerky. I think a smoother, but computationally slower way to do this would be to do a fully calculated update using alpha instead of step time, and storing that in the interpPosition vector. But only do that if necessary.