这是 FPS 独立游戏循环的良好实现吗?
Posted
技术标签:
【中文标题】这是 FPS 独立游戏循环的良好实现吗?【英文标题】:Is this a good implementation of a FPS independant game loop? 【发布时间】:2010-10-18 16:05:21 【问题描述】:我目前有一些接近于以下实现的 FPS 独立游戏循环,用于基于物理的游戏。它在我测试过的几乎每台计算机上都运行良好,在帧速率下降时保持游戏速度一致。但是,我将移植到嵌入式设备,这可能会在视频方面更加困难,我想知道它是否仍然会减少芥末。
编辑:
对于这个问题,假设 msecs() 返回程序运行的毫秒数。不同平台上毫秒的实现是不同的。这个循环也在不同的平台上以不同的方式运行。
#define MSECS_PER_STEP 20
int stepCount, stepSize; // these are not globals in the real source
void loop()
int i,j;
int iterations =0;
static int accumulator; // the accumulator holds extra msecs
static int lastMsec;
int deltatime = msec() - lastMsec;
lastMsec = msec();
// deltatime should be the time since the last call to loop
if (deltatime != 0)
// iterations determines the number of steps which are needed
iterations = deltatime/MSECS_PER_STEP;
// save any left over millisecs in the accumulator
accumulator += deltatime%MSECS_PER_STEP;
// when the accumulator has gained enough msecs for a step...
while (accumulator >= MSECS_PER_STEP)
iterations++;
accumulator -= MSECS_PER_STEP;
handleInput(); // gathers user input from an event queue
for (j=0; j<iterations; j++)
// here step count is a way of taking a more granular step
// without effecting the overall speed of the simulation (step size)
for (i=0; i<stepCount; i++)
doStep(stepSize/(float) stepCount); // forwards the sim
【问题讨论】:
我知道我可能不应该将时间的东西存储在整数中,但这并不是我在这里真正要问的 【参考方案1】:我只有几个 cmets。首先是你没有足够的cmets。有些地方不清楚您要做什么,因此很难说是否有更好的方法可以做到这一点,但我会在遇到这些问题时指出这些。首先,虽然:
#define MSECS_PER_STEP 20
int stepCount, stepSize; // these are not globals in the real source
void loop()
int i,j;
int iterations =0;
static int accumulator; // the accumulator holds extra msecs
static int lastMsec;
这些没有被初始化为任何东西。可能出现为 0,但您应该初始化它们。此外,与其将它们声明为静态,不如考虑将它们放入一个结构中,然后通过引用传递给 loop
。
int deltatime = msec() - lastMsec;
由于lastMsec
没有(已初始化并且可能为 0),这可能一开始是一个大增量。
lastMsec = msec();
这一行和最后一行一样,调用msec
。这可能意味着“当前时间”,并且这些调用足够接近以至于两个调用的返回值可能相同,这可能也是您所期望的,但是您仍然调用了该函数两次。您应该将这些行更改为 int now = msec();
int deltatime = now - lastMsec;
lastMsec = now;
以避免两次调用此函数。当前时间获取函数的开销通常比您想象的要高得多。
if (deltatime != 0)
iterations = deltatime/MSECS_PER_STEP;
accumulator += deltatime%MSECS_PER_STEP;
您应该在此处发表评论,说明这是做什么的,以及上面的评论 这说明了变量的含义。
while (accumulator >= MSECS_PER_STEP)
iterations++;
accumulator -= MSECS_PER_STEP;
这个循环需要注释。它也不需要在那里。看来它可以替换为iterations += accumulator/MSECS_PER_STEP;
accumulator %= MSECS_PER_STEP;
。与任何具有硬件除法的机器上的循环相比,除法和模数应该在更短且更一致的时间内运行(很多机器都这样做)。
handleInput(); // gathers user input from an event queue
for (j=0; j<iterations; j++)
for (i=0; i<stepCount; i++)
doStep(stepSize/(float) stepCount); // forwards the sim
在独立于输入的循环中执行步骤,如果游戏执行缓慢并落后,则会导致游戏无响应。至少看起来,如果游戏落后于所有输入,则所有输入将开始堆积并一起执行,并且所有游戏内时间都将一并流逝。这是一种不太优雅的失败方式。
另外,我可以猜到j
循环(外循环)是什么意思,但内循环我不太清楚。还有,传递给doStep
函数的值——这是什么意思。
这是最后一个花括号。我觉得它看起来很孤独。
我不知道调用您的 loop
函数时会发生什么,这可能超出您的控制范围,这可能会决定该函数的作用和外观,但如果不是,我希望您将重新考虑结构。我相信更好的方法是拥有一个重复调用但当时只有一个事件的函数(在相对较短的时间内定期发布)。这些事件可以是用户输入事件或定时器事件。用户输入事件只是设置了对下一个计时器事件作出反应。 (当你没有任何事件来处理你的睡眠时)
您应该始终假设每个计时器事件在同一时间处理,即使如果处理落后,这里可能会出现一些偏差。您可能会注意到这里的主要奇怪之处是,如果游戏在处理计时器事件时落后,然后又赶上来,那么游戏中的时间可能会变慢(低于实时),然后加快(到实时),并且然后放慢速度(到实时)。
处理这个问题的方法包括一次只允许一个计时器事件在事件队列中,这会导致时间看起来变慢(低于实时)然后加速(回到实时)而没有超速区间。
另一种方法,在功能上与您所拥有的类似,是处理每个计时器事件的最后一步是将下一个计时器事件排队(请注意,其他人不应发送计时器事件除了对于第一个,如果这是您选择实现游戏的方式)。这意味着取消定时器事件之间的常规时间间隔,并限制程序休眠的能力,因为至少每次检查事件队列时都会有一个定时器事件要处理。
【讨论】:
绝妙的答案 - 这里有很多值得深思的地方。我会添加一些cmets。以上是关于这是 FPS 独立游戏循环的良好实现吗?的主要内容,如果未能解决你的问题,请参考以下文章
FPS 和 UPS 有啥区别?我应该在游戏循环中跟踪 UPS 吗?