我对带有增量时间和插值的固定时间步长的解决方案是错误的吗?

Is my solution to fixed timestep with delta time and interpolation wrong?

我正在尝试使用 用于物理和插值的固定增量时间 在渲染状态之前编写简单循环。我在固定时间步长的游戏教程中使用 Gaffer,我试图理解它并使其发挥作用。

float timeStep = 0.01;
float alpha = 1.0;
while (isOpen()) {
    processInput();
    deltaTime = clock.restart(); // get elapsed time
    if (deltaTime > 0.25) { deltaTime = 0.25; } // drop frame guard
    accumulator += deltaTime;
    while (accumulator >= timeStep) {
        // spritePosBefore = sprite.getPosition();
        accumulator -= timeStep;
        // sprite.move(velocity * timeStep, 0);
        // spritePosAfter = sprite.getPosition();
    }
    if (accumulator > timeStep) { alpha = accumulator / timeStep; } else { alpha = 1.0; }
    // sprite.setPosition(Vector2f(spritePosBefore * (1 - alpha) + spritePosAfter * alpha));
    clear();
    draw(sprite);
    display();
}

现在,一切看起来都很好。我有固定的物理时间步长,在物理更新后尽可能绘制并在两个位置之间进行插值。它应该可以完美运行,但我仍然可以看到精灵卡顿,甚至偶尔会后退一个像素。为什么会这样?我的代码有问题吗?我花了两天时间试图了解游戏循环,这将确保我的动作完美无缺,但它似乎并没有像我想象的那样工作。知道可以改进什么吗?

我不认为你这样做的方式一定是错误的,但它看起来有点过于复杂。我不明白你到底想做什么,所以我只想分享我在我的 SFML 应用程序中实现“固定时间步长”的方式。
以下是最简单的方法,对于大多数应用程序来说“足够好”。虽然它不是最精确的(测量时间和实际时间之间可能会有一点误差):

    sf::Clock clock;    
    sf::Event event;
    while (window_.isOpen()) {
        while (window_.pollEvent(event)) {}

        if (clock.getElapsedTime().asSeconds() > FLT_FIXED_TIME_STEP) {
            clock.restart();
            update(FLT_FIXED_TIME_STEP);
        }

        render();
    }

如果您真的需要精度,您可以添加一个浮点变量作为“缓冲区”:

    sf::Clock clock;    
    sf::Event event;
    float timeBeforeNextStep = 0.f; // "buffer"
    float timeDilation = 1.f; // Useful if you want to slow or speed up time ( <1 for slowmo, >1 for speedup)
    while (window_.isOpen()) {
        while (window_.pollEvent(event)) {}

        timeBeforeNextStep -= clock.restart().asSeconds() * timeDilation;
        if (timeBeforeNextStep < FLT_FIXED_TIME_STEP) {
            timeBeforeNextStep += FLT_FIXED_TIME_STEP; // '+=', not '=' to make sure we don't lose any time.
            update(FLT_FIXED_TIME_STEP);

            // Rendering every time you update is not always the best solution, especially if you have a very small time step.
            render();
        }
    }

您可能想要使用另一个缓冲区进行渲染(例如,如果您想要 运行 正好是 60 FPS)。

您应该删除 if 语句并始终计算 alpha; if 语句将永远不会执行,因为在退出 while 循环后条件始终为 false!

循环后累加器将介于 0 和 timeStep 之间,因此您只需绘制最新位置而不是插值。