带插值的游戏循环 - 奇怪的后退

Posted

技术标签:

【中文标题】带插值的游戏循环 - 奇怪的后退【英文标题】:Game loop with interpolation - weird step back 【发布时间】:2017-09-08 16:20:45 【问题描述】:

我已阅读有关应用于游戏循环的插值并尝试自己实现它。它看起来与我预期的几乎相同,但是当对象结束其运动时,会发生奇怪的后退。我决定在这里粘贴完整的源代码,因为这个问题可能是由一切引起的。

#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();

我不知道是什么导致了这种问题。我期待您的帮助。

【问题讨论】:

你看过这个系列的帖子吗:gafferongames.com/post/fix_your_timestep 是的,甚至不止一次,但也许我误解了什么。 【参考方案1】:

您的interpolate 函数可以多次计算时间间隔的某些部分。

由于accumulator 仅在每个timePerFrame 滴答声中重置,因此快速循环速率可以多次添加更小的间隔。如果主循环在 0.01 秒内运行,则第一次调用 interpolate 在插值因子中使用该 0.01。下一次,它使用 0.02(总共增加 0.03)。这一直持续到有足够的时间让update 使用 0.1 秒(时间步长)更新位置。由于interpolate 添加的时间比这多,所以对象会跳回来。

interpolate 应该只添加当前步骤的时间,而不是完全累积的时间。 (另外,不是调用now 来获取每个循环的开始时间,而是应该将前一个循环的用于结束时间的now 值用作下一个循环的开始时间。否则你会丢失偶尔的时钟滴答声它在一个循环的结束和下一个循环的开始之间变化。)

【讨论】:

感谢您的快速回复。从你的回答中,我明白了一些事情。我的意思是在主循环结束时添加一个包含时间点的新变量是个好主意。但是我不知道您所说的“在当前步骤中添加”是什么意思。在下面的链接中,有一张图片描述了我的思维方式。我的意思是插值位置不应大于下一帧设置的位置。 i.imgur.com/jGa2Hyw.png @NaulPewman interpolate 函数应该只添加当前步骤的时间间隔 - 您添加到 accumulator 的数量。换句话说,由于它已经考虑到最后一次调用interpolate 的时间,它只想考虑从那时起经过的时间。 您能否发布一些示例代码,它应该如何工作?那样想会更好。

以上是关于带插值的游戏循环 - 奇怪的后退的主要内容,如果未能解决你的问题,请参考以下文章

平滑 Android 游戏循环

为 ncurses 游戏编写游戏循环?

Java 游戏循环口吃

多人游戏 - 客户端插值计算?

游戏编程精粹学习 - 使用定点颜色插值模拟实时光照

如果有固定的时间步长和插值,则进行多个游戏更新的目的