2016-09-27 35 views
1

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

正如你看到的我是知道的垂直同步,但它不帮助这么多。我的固定时间循环是否错误?任何帮助表示赞赏!

+0

你并不需要自己'SF :: Window'has称为方法来实现这个'setFramerateLimit' – DogeAmazed

+0

谢谢你了迅速的回答。我读过使用这个函数不推荐。 –

+0

忽略我最后的评论,Bosslard的回答启发了我。 –

回答

1

您是否尝试过不启用垂直同步?垂直同步和实施的时间步可能会通过试图同时控制帧速率而相互干扰。在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的明显需要,如果有撕裂)。经过研究,并与代码乱搞日子里,我学到的是解决这个问题的几件事情:

  • 禁用垂直同步(当然我们已经知道这一点)
  • 用Fraps
  • 录制节目
  • window.display()
  • 调用glFinish()在创建一个窗口或者全屏或无边界(与SF ::风格::无),在同样大小的桌面
  • 禁用Windows Aero主题(当然这仅适用于Windows)

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

所以我的建议:

  • 使用glFinish()display()调用后,如果使用窗口模式,虽然这有一个点球,它停止了CPU与GPU的同步,并监控时,它可以异步地处理和不间断的,但如果你的程序不是CPU密集型的,那么这应该不是什么大问题。在SFML中,您可以包含“SFML/OpenGL.hpp”以访问此功能。虽然如果你使用的是Windows(我认为Vista和7,我不知道8或10是如何处理的),然而使用Aero主题vsync显然是自动启用的,你可能没有开始。您使用什么操作系统?
  • 使用全屏模式,只接受在窗口模式下会出现口吃。它在实际的游戏或其他图形应用程序中可能甚至不会引人注目,与在空白背景上不断来回移动的刚性高对比度广场相反。我知道在我现在正在做的游戏中,我有同样的问题,但我几乎没有注意到它。
  • 阅读有关该问题,看看你是否能找到一个真正的修复。无可否认,使用glFinish是一种混乱,在窗口模式下处理口吃或撕裂可能是不可接受的。我不完全确定glFinish如何设法消除口吃,并且通常不建议调用该功能。

一些进一步阅读:

+0

不知道如何实现这个功能是一个错误。谢谢,我现在确定,我不应该在同一时间使用垂直同步的固定时间步长。但还有一件事情是屏幕撕裂。如果禁用垂直同步,则视觉伪影变得可见。我在YouTube上发现了一些显示相同问题的视频:[link](https://www.youtube.com/watch?v=Kvgz_hQCkZ4)。任何建议? –

+0

其实你可以更新你的物理,逻辑等尽可能多的仍然呈现在60或30帧/秒。这是一个很好的阅读:http://gafferongames.com/game-physics/fix-your-timestep/ – Zouch

+0

@ Zouch我在这里看到的问题是如何启用vsync来修复屏幕撕裂,同时也保持平滑的动画,因为这里似乎是vsync导致了一个不寻常的口吃。我不能为提问者说话,但是我已经在这篇文章中提供了一些例子,并且似乎没有解决这个问题(尽管我可能需要更多时间来查看问题)。我现在也诚实地处于亏损状态。 – Bosslard