我有一个简短的问题。我正在学习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;
}

如您所见,我了解垂直同步,但是并没有太大帮助。我的固定时间步长循环错了吗?任何帮助表示赞赏!

最佳答案

您是否尝试过不启用垂直同步?垂直同步和您已实现的时间步可能都试图同时控制帧速率而互相干扰。在Windows的SFML tutorial page上,它们指出:

“切勿同时使用setVerticalSyncEnabledsetFramerateLimit!它们会严重混合并使情况更糟。”

正如您从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中,您可以包括“SFML / OpenGL.hpp”以访问此功能。虽然如果您使用的是Windows(我相信是Vista和7,我不知道Windows 8或10是如何处理的),那么在使用Aero主题时,vsync显然会自动启用,因此您可能一开始就不会哭。您使用什么操作系统?
  • 使用全屏显示,只接受窗口模式下会出现卡顿现象。与在空白背景上来回连续移动的刚性高对比度方块相反,在实际游戏或其他图形应用程序中,它甚至可能不会引起注意。我知道在游戏中我现在正在遇到同样的问题,但是我几乎没有注意到它。
  • 阅读有关此问题的信息,看看是否可以找到真正的解决方案。可以肯定的是,使用glFinish有点不合时宜,在窗口模式下处理口吃或撕裂可能是 Not Acceptable 。我不确定glFinish如何消除卡顿现象,因此通常不建议调用该函数。

  • 一些进一步的阅读:
  • https://superuser.com/questions/558007/how-does-windows-aero-prevent-screen-tearing
  • http://www.cplusplus.com/forum/general/108295/
  • http://forums.nesdev.com/viewtopic.php?f=3&t=9262#p98808(尤其是
  • 发帖人
  • this(SFML内部调用的内容
    启用vsync)
  • https://www.opengl.org/wiki/Swap_Interval
  • http://www.gamedev.net/topic/381574-sdl-no-vsync-in-windowed-mode/
  • http://larian.com/forums/ubbthreads.php?ubb=showflat&Number=482323
  • 关于c++ - SFML +固定时间步长=滞后?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39734378/

    10-13 08:22