SFML渲染性能问题

3

我过去一周一直在制作我的游戏核心部分,但我因渲染效果不够好而遇到难题。移动时卡顿、出现撕裂,总体上延迟很大。我认为这可能不是游戏引擎的问题,因此我使用非常简单的游戏循环进行了渲染测试:

        sf::RenderWindow window(sf::VideoMode(1024, 768), "Testing");
                window.setVerticalSyncEnabled(true);
        sf::Clock clock;
        sf::Event event;
        float elapsed;
        while(window.isOpen())
        {
                elapsed += clock.restart().asSeconds();
                std::cout << 1.f/elapsed << std::endl;
                while(elapsed > 1.f/60.f)
                {
                        while(window.pollEvent(event))
                        {
                                if (event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape)
                                {
                                        window.close();
                                }
                        }
                        elapsed -= 1.f/60.f;
                }
                window.clear();
                window.display();
        }

FPS起始值为40,上升到60,然后降回30,再次递增并重复。如果我将VSynct设置为false,我会得到30-500 fps之间的任何值。我不确定是否正确测试了帧率或者我的nvidia驱动程序出了问题(我尝试过两次重新安装但没有改变)。非常感谢您的帮助!


我不太明白你为什么要执行 elapsed -= 1.f/60.f;。这背后的逻辑是什么?我不理解你为什么要减去一个常数。 - luk32
看看这个。http://gameprogrammingpatterns.com/game-loop.html - Veritas
2个回答

1
你向我指出了一份与你的代码类似但写法不同的材料。
来源:gameprogrammingpatterns.com/game-loop.html
double previous = getCurrentTime();
double lag = 0.0;
while (true)
{
  double current = getCurrentTime();
  double elapsed = current - previous;
  previous = current;
  lag += elapsed;

  processInput();

  while (lag >= MS_PER_UPDATE)
  {
    update();
    lag -= MS_PER_UPDATE;
  }

  render();
}

你似乎在同时使用一个变量 elapsed 来表示 elapsedlag,这让我感到困惑。你对 elapsed 的操作使其无法用于计时的目的。我认为你的代码应该更像这样:
    sf::RenderWindow window(sf::VideoMode(1024, 768), "Testing");
            window.setVerticalSyncEnabled(true);
    sf::Clock clock;
    sf::Event event;
    float lag;
    float elapsed;

    while(window.isOpen())
    {
            lag = elapsed = clock.restart().asSeconds();
            std::cout << 1.f/elapsed << std::endl;
            while(lag > 1.f/60.f)
            {
                    while(window.pollEvent(event))
                    {
                            if (event.type == sf::Event::Closed || event.key.code == sf::Keyboard::Escape)
                            {
                                    window.close();
                            }
                    }
                    lag -= 1.f/60.f;
            }
            window.clear();
            window.display();
    }

我还不确定这是否正确。我不知道clock.restart().asSeconds()确切的作用是什么。就我个人而言,我会像示例一样逐行实现它。为什么要重新设计已经工作的代码呢?
编辑:楼主确认,使用elapsed进行“节流”会破坏其作为时间测量变量的目的。

restart()函数返回经过的时间,因此我不必使用滞后变量。然而,它确实不能正确地用于像这样计算帧数,我完全忽略了这一点。现在我可以始终获得60 fps,非常感谢! - Veritas
所以我做对了,耶。感谢确认。我不确定是否正确,因为您在使用+=与经过时间(elapsed)一起使用,但我认为如果“restart”确实重新启动了时钟,则不需要将其加总。 - luk32

1
这是我认为正在发生的事情。如果我正确理解你的代码,你想保持模拟的时间步长恒定 - 每秒60次。你使用了一种累加器来跟踪应该运行循环的次数。使用这样的累加器的问题在于你必须记住你的模拟循环需要时间。为了说明为什么可能会有问题,让我们来看一些情况。为了清晰起见,我将使用20毫秒而不是16.6666(1/60秒)作为时间步长。假设:
  1. 您的模拟循环需要运行10毫秒。第一次 elapsed >= 20,它会被重置并运行您的模拟循环。下一次回到顶部时,elapsed == 10 + c,其中 c 是一些小数,并且跳过。最终,elapsed >= 20再次出现并重复。一切正常。
  2. 您的模拟循环需要精确运行20毫秒。elapsed达到20,内部循环需要20毫秒才能运行。下一次运行循环时,elapsed == 20。它重复。基本上一切正常。
  3. 您的模拟循环需要运行40毫秒。当elapsed达到20时,内部循环运行,需要40毫秒。然后,外部循环再次运行,这次elapsed == 40。然后,内部循环运行两次,需要80毫秒。现在elapsed为80。内部循环运行4次,需要160毫秒。现在elapsed为160。内部循环运行8次,需要320毫秒...以此类推。
当然,模拟循环所需的时间永远不会像这些示例中那样是恒定的,但思想是相同的。如果您可以假设循环所需的时间不会比您的模拟步骤运行时间长得多,那么就没问题了。一旦循环运行时间超过1/60秒,您就会开始获得一种反馈循环,外部循环需要花费更长的时间才能完成。
我通常解决“帧率依赖移动”这种问题的方式是向所有我的update()类型函数传递一个增量时间,无论它们在哪里。然后,所有的移动/攻击/任何操作都依赖于该增量时间。例如,对于移动,我会有类似以下的代码:
const static pixelsPerSecond = 100
update(float delta) { //delta is seconds
    x += moveX * pixelsPerSecond * delta;
    y += moveY * pixelsPerSecond * delta;
}

编辑:刚看到你发布的链接,提到了我推荐的变量时间步长的缺点。然而,通常情况下,如果您不进行精确的物理模拟或多人游戏,它应该可以正常工作。(有趣的是,您的链接包含一个指向Glenn Fielder's article的链接,其中也提到了我上面描述的“死亡螺旋”)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接