【问题标题】:Is my solution to fixed timestep with delta time and interpolation wrong?我使用增量时间和插值的固定时间步的解决方案是否错误?
【发布时间】:2020-07-01 23:32:50
【问题描述】:

我正在尝试在渲染状态之前使用用于物理和插值的固定增量时间编写简单的循环。我在固定时间步长的游戏教程中使用 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();
}

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

【问题讨论】:

标签: c++ game-physics sfml timedelta linear-interpolation


【解决方案1】:

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

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

【讨论】:

  • 是的,我在三天前发现了这个并删除了这个声明^^
【解决方案2】:

我不认为你这样做的方式一定是错误的,但它看起来有点过于复杂。我不明白你到底想做什么,所以我只想分享我在我的 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 的速度运行)。

【讨论】:

  • 在第一个示例中,我认为这不是实现具有固定时间步长的循环的好方法。例如,如果时间步长是 1/60 秒,帧之间的时间是 0.25 秒,那么您正在使用固定值更新物理,您在游戏中获得 1/60 秒的时间,在现实生活中为 0.25 秒。第二个版本似乎是正确的,但即使 timeBeforeNextStep 小于固定时间步,您仍然可以在绘图之前使用这个剩余值进行插值。这是我正在努力实现的目标,但我不确定我的实现是否正确,因为我仍然觉得动作有点迟缓。
  • @Advent 好的,现在我明白你要做什么了,我第一次不明白:)。您需要做的是将您的精灵位置设置为posA + ((posB - posA) / distanceAB) * timeSinceLastStep,其中“timeSinceLastStep 是自从您的精灵处于 posA 以来经过的时间。我从来没有这样做过,但我会这样做。祝你好运!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-28
  • 2015-12-16
  • 1970-01-01
  • 2013-06-12
  • 2020-11-17
  • 2020-09-10
  • 1970-01-01
相关资源
最近更新 更多