使用sleep在单个线程中将逻辑/更新与渲染/绘制代码分开


9

我读过,游戏对象的速度不应该受到FPS的阻碍,而应该基于时间。如何分隔更新/绘制代码以在不限制绘制速率的情况下最大化性能,并提供基于时间的恒定逻辑更新速率?

我当前的伪代码如下

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

问题在于绘图代码阻碍了update()速率的性能。而且它消耗了100%的cpu,因为如果引发了睡眠,它将同时中断两个绘图/逻辑功能。

我也在使用SDL,它似乎没有vsync选项。我也听说过固定和可变时步的术语,但是我不确定用sleep()如何做到这一点。


1
您不需要浪费100%的CPU功率来等待,如果ticksElapsed()<100,则在while循环的末尾放置sleep(0)。如果没有其他线程可以立即返回线程想跑。但不再浪费100%的CPU能力。
Maik Semder 2011年

但是,这种1线程设置的最佳解决方案是使用vsync,如果无法进行vsync,则循环调用sleep(0)直到达到目标帧速率,然后进行更新和绘制
Maik Semder 2011年

Answers:


3

在您的代码段中,您似乎正试图通过忙于等待绘图和更新少于15ms(60fps)的时间在固定时间步进模式下运行游戏。这是可能的,而且您猜对了,无法使用睡眠呼叫来完成此操作,因为您不完全知道要睡多长时间。繁忙的等待循环是一个很好的解决方案。

但是,考虑更新和绘图超过15毫秒的情况,现在您的游戏绘图和更新速度变慢。现在,您可以执行以下两项操作:检测到该状态并丢帧(跳过绘图并直接进行更新,直到再次同步),但是,如果计算机只是在变慢,它将永远不会赶上。

另一种解决方案是使更新逻辑固定时间独立。您不需要为此使用单独的线程,只需要重新指定事物移动的速度即可。您应该每秒使用50个像素,而不是每个刻度5个像素。您将需要一个高精度计时器来实现此目的,并且所有更新逻辑都应能够访问该计时器,以查看自上次更新以来已过去了多少时间。

基本上,您来自:

void UpdatePlayer()
 player.x += 10;

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;

因此,我的引擎将始终消耗100%,对此我无能为力吗?
Oskenso Kashi 2011年

1
它会消耗调度程序所允许的尽可能多的周期,但这并不是真正的问题,因为在玩游戏时您无法真正做很多其他事情:)。
罗伊·T。

@Oskenso但是,如果使用多个线程是一个问题,那么主线程将无法让其他线程尽可能多地运行,从而在while循环中浪费了很多计算能力,您确实应该考虑睡眠
Maik Semder 2011年

@Maik Semder:您是否有解决睡眠不准确的方法?经过睡眠间隔后,线程即可运行。但是,不能保证就绪线程可以立即运行。这取决于调度程序。当您使用两个线程时,还有其他解决方案,有关这方面的文章,请参见:altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.

1
@Roy sleep(0)是解决方案。如果没有其他要运行的线程(Sleep WinAPI),它将立即返回,并为其他线程提供运行的机会。如果另一个线程不会给主线程以返回运行的机会,那么您就遇到了线程问题,但是通过首先不调用sleep来阻塞其他所有事情,这会使情况变得更糟,并且很难解决。关键是要调用sleep(0)并测试经过的时间,直到达到目标固定帧速率,这样您才不会浪费100%的CPU来等待。
Maik Semder 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.