SFML +固定时间步长=滞后?

时间:2016-09-27 21:12:01

标签: c++ game-engine sfml

我有一个简短的问题。我正在学习SFML,我使用它制作了一些应用程序。有一天,我学会了在编写游戏时使用固定时间步骤,如果有效的话会很棒。但。即使我正在制作简单的项目,如从窗口左侧移动到右侧的方形 - 它是滞后的!可能是造成这个问题的原因是什么?这是我的代码:

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "Window");
    window.setVerticalSyncEnabled(true);
    sf::Event event;
    sf::Clock clock;
    sf::Time accumulator = sf::Time::Zero;
    const sf::Time timePerFrame = sf::seconds(1.f / 60.f);
    sf::RectangleShape square;
    square.setSize(sf::Vector2f(32, 32));
    square.setOrigin(16, 16);
    square.setPosition(400, 300);
    square.setFillColor(sf::Color::Red);
    int direction = 1;
    int speed = 300;
    while(window.isOpen())
    {
        while(window.pollEvent(event))
        {
            if(event.type == sf::Event::Closed)
            {
                window.close();
            }
        }
        while(accumulator >= timePerFrame)
        {
            if(square.getPosition().x <= 16 || square.getPosition().x >= 784) direction *= (-1);
            square.move(sf::Vector2f(speed * direction * timePerFrame.asSeconds(), 0));
            accumulator -= timePerFrame;
        }
        accumulator += clock.restart();
        window.clear(sf::Color::Black);
        window.draw(square);
        window.display();
    }
    return 0;
}

正如您所看到的,我知道垂直同步,但它并没有多大帮助。我的固定时间步循环错了吗?任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:1)

您是否尝试过不启用垂直同步?垂直同步和您实现的时间步长都可能会同时干扰,同时尝试控制帧速率。在SFML tutorial page for windows上,他们声明:

&#34;永远不要同时使用setVerticalSyncEnabledsetFramerateLimit!他们会严重混合并使事情变得更糟。&#34;

正如您可以从SFML的源代码中看到的那样:

void Window::setFramerateLimit(unsigned int limit)
{
    if (limit > 0)
        m_frameTimeLimit = seconds(1.f / limit);
    else
        m_frameTimeLimit = Time::Zero;
}

void Window::display()
{
    // Display the backbuffer on screen
    if (setActive())
        m_context->display();

    // Limit the framerate if needed
    if (m_frameTimeLimit != Time::Zero)
    {
        sleep(m_frameTimeLimit - m_clock.getElapsedTime());
        m_clock.restart();
    }
}

SFML的setFramerateLimit功能与您的时间步长大致相同,因此我不相信这里的情况会有所不同。

在我的机器上测试你的代码后,我确实看到了方块上的轻微延迟,但是,禁用垂直同步解决了这个问题。

编辑:事实证明这个问题比我原先想象的要复杂得多(而且我也忽略了如果出现撕裂,显然需要vsync)。经过几天的研究和乱码,我已经了解了一些解决问题的方法:

  • 禁用vsync(当然我们已经知道了)
  • 使用Fraps录制节目
  • glFinish()
  • 之后致电window.display()
  • 以全屏或无边框(使用sf :: Style :: None)创建与桌面大小相同的窗口
  • 禁用Windows Aero主题(当然这仅适用于Windows)

除了第一个之外的所有内容都会修复它,即使启用了vsync也是如此。这里的主要问题似乎是vsync在窗口模式下无法正常工作,显然它是一个限制因为程序必须与其他不必启用vsync的程序共享屏幕空间。

所以我的建议是:

  • 如果使用窗口模式,则在每次glFinish()调用后使用display(),虽然这会受到惩罚,但它会暂停CPU并将其与GPU同步并监视它何时可以异步和不间断地处理,但是如果你的程序不是特别密集CPU,那么这不应该是一个大问题。在SFML中,您可以包含&#34; SFML / OpenGL.hpp&#34;访问此功能。虽然如果你使用Windows(我认为Vista和7,我不知道8或10如何处理事情),那么使用Aero主题vsync时显然会自动启用,你可能没有开始尝试。你在使用什么操作系统?
  • 使用全屏,只需接受窗口模式下的口吃。它甚至可能在实际游戏或其他图形应用程序中不明显,而不是在空白背景上连续来回移动的刚性高对比度正方形。我知道在我正在进行的游戏中,我遇到了同样的问题,但我几乎没有注意到它。
  • 阅读有关该问题的信息,看看是否能找到真正的修复方法。不可否认,使用glFinish有点像kludge,并且在窗口模式下处理口吃或撕裂可能是不可接受的。我并不完全确定glFinish如何设法消除口吃,而且通常从未建议过这个功能。

进一步阅读: