无论您使用什么图形库,我认为实现这一点的一个好方法是在游戏循环中进行单个时钟测量,以考虑每个滴答声(毫秒)。这样一来,平均 fps 将正好是《半条命 2》中的极限。希望下面的代码片段能解释我在说什么:
//FPS limit
unsigned int FPS = 120;
//double holding clocktime on last measurement
double clock = 0;
while (cont) {
//double holding difference between clocktimes
double deltaticks;
//double holding the clocktime in this new frame
double newclock;
//do stuff, update stuff, render stuff...
//measure clocktime of this frame
//this function can be replaced by any function returning the time in ms
//for example clock() from <time.h>
newclock = SDL_GetTicks();
//calculate clockticks missing until the next loop should be
//done to achieve an avg framerate of FPS
// 1000 / 120 makes 8.333... ticks per frame
deltaticks = 1000 / FPS - (newclock - clock);
/* if there is an integral number of ticks missing then wait the
remaining time
SDL_Delay takes an integer of ms to delay the program like most delay
functions do and can be replaced by any delay function */
if (floor(deltaticks) > 0)
SDL_Delay(deltaticks);
//the clock measurement is now shifted forward in time by the amount
//SDL_Delay waited and the fractional part that was not considered yet
//aka deltaticks
the fractional part is considered in the next frame
if (deltaticks < -30) {
/*dont try to compensate more than 30ms(a few frames) behind the
framerate
//when the limit is higher than the possible avg fps deltaticks
would keep sinking without this 30ms limitation
this ensures the fps even if the real possible fps is
macroscopically inconsitent.*/
clock = newclock - 30;
} else {
clock = newclock + deltaticks;
}
/* deltaticks can be negative when a frame took longer than it should
have or the measured time the frame took was zero
the next frame then won't be delayed by so long to compensate for the
previous frame taking longer. */
//do some more stuff, swap buffers for example:
SDL_RendererPresent(renderer); //this is SDLs swap buffers function
}
我希望这个 SDL 示例有所帮助。重要的是每帧只测量一次时间,因此要考虑每一帧。我建议在一个函数中模块化这个时间,这也使你的代码更清晰。这段代码没有评论,因为他们只是在最后一个惹恼了你:
unsigned int FPS = 120;
void renderPresent(SDL_Renderer * renderer) {
static double clock = 0;
double deltaticks;
double newclock = SDL_GetTicks();
deltaticks = 1000.0 / FPS - (newclock - clock);
if (floor(deltaticks) > 0)
SDL_Delay(deltaticks);
if (deltaticks < -30) {
clock = newclock - 30;
} else {
clock = newclock + deltaticks;
}
SDL_RenderPresent(renderer);
}
现在你可以在你的 mainloop 中调用这个函数,而不是你的 swapBuffer 函数(SDL_RenderPresent(renderer)
在 SDL 中)。在 SDL 中,您必须确保该SDL_RENDERER_PRESENTVSYNC
标志已关闭。此函数依赖于全局变量FPS
,但您可以考虑其他存储方式。我只是把整个东西放在我图书馆的命名空间里。
如果由于 30ms 限制为deltaticks
. deltaticks
限制是必需的。当 FPS 限制高于实际帧率deltaticks
时会无限下降。同样,当帧速率再次上升到 FPS 限制之上时,代码将尝试通过立即渲染每一帧来补偿损失的时间,从而导致巨大的帧速率,直到deltaticks
上升回零。您可以修改 30ms 以满足您的需求,这只是我的估计。我用 Fraps 做了几个基准测试。它确实适用于所有可以想象的帧速率,并根据我的测试提供漂亮的结果。
我必须承认我昨天才编写了这个代码,所以不太可能有某种错误。我知道这个问题是 5 年前提出的,但给出的答案并没有说明我的情况。也可以随意编辑这篇文章,因为这是我的第一篇文章,可能有缺陷。
编辑:我注意到SDL_Delay
在某些系统上非常非常不准确。我听说过一个在 android 上延迟太多的案例。这意味着我的代码可能无法移植到您想要的所有系统。