我有一个简短的问题。我正在学习 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;
}
如您所见,我了解垂直同步,但并没有太大帮助。我的固定时间步长循环错了吗?感谢您的帮助!
最佳答案
您是否尝试过不启用垂直同步?垂直同步和您实现的时间步长可能会因为同时尝试控制帧速率而相互干扰。关于SFML tutorial page对于 window ,他们说:
“切勿同时使用 setVerticalSyncEnabled
和 setFramerateLimit
!它们会严重混合并使事情变得更糟。”
正如您从 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
函数的实现方式与您的时间步几乎相同,因此我认为这里的情况不会有任何不同。
在我的机器上测试您的代码时,我确实看到方 block 上有轻微的延迟,但是,禁用垂直同步解决了这个问题。
编辑:事实证明,这个问题比我原先想象的要复杂(而且我也忽略了在撕裂情况下明显需要 vsync 的情况)。经过几天的研究和修改代码,我了解到一些可以解决问题的方法:
- 禁用 vsync(当然我们已经知道这一点)
- 用Fraps录制节目
- 在
window.display()
之后调用glFinish()
- 创建与桌面大小相同的全屏或无边框(使用 sf::Style::None)窗口
- 禁用 Windows Aero 主题(当然这只适用于 Windows)
即使启用垂直同步,除了第一个之外的所有这些都会修复它。这里的主要问题似乎是 vsync 在窗口模式下无法正常工作,显然这是一个限制,因为程序必须与其他不一定启用 vsync 的程序共享屏幕空间。
所以我的建议是:
- 如果使用窗口模式,则在每次
display()
调用后使用glFinish()
,尽管这会带来损失,但它会暂停 CPU 并将其与 GPU 和监视器同步当它可以异步且不间断地处理时,但如果您的程序不是特别占用 CPU,那么这应该不是什么大问题。在 SFML 中,您可以包含“SFML/OpenGL.hpp”以访问此功能。尽管如果您使用的是 Windows(我相信是 Vista 和 7,但我不知道 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由 rainwarrior 发布)
- https://www.opengl.org/wiki/Swap_Interval (SFML 内部调用的 启用垂直同步)
- http://www.gamedev.net/topic/381574-sdl-no-vsync-in-windowed-mode/
- http://larian.com/forums/ubbthreads.php?ubb=showflat&Number=482323
- http://dev.dota2.com/archive/index.php/t-11450.html
关于c++ - SFML + 固定时间步 = 滞后?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39734378/