这是FPS独立游戏循环的良好实现吗?

时间:2010-10-18 16:05:22

标签: c game-loop

我目前有一些接近以下针对基于物理的游戏的FPS独立游戏循环的实现。它几乎可以在我测试的每台计算机上运行良好,在帧速率下降时保持游戏速度一致。然而,我将移植到嵌入式设备,这可能会对视频造成更大的困难,我想知道它是否仍会削减芥末。

编辑:

对于这个问题,假设msecs()返回程序运行的时间(以毫秒为单位)。 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 个答案:

答案 0 :(得分:6)

我只是有一些评论。首先是你没有足够的评论。有些地方不清楚你要做什么,所以很难说是否有更好的方法去做,但是当我来到他们身边时我会指出那些。首先,但是:

#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函数会发生什么事情,这可能是你无法控制的,这可能决定了这个功能是什么以及它看起来如何,但如果没有,我希望你会重新考虑这个结构。我认为更好的方法是拥有一个重复调用的函数,但当时只有一个事件(在相对较短的时间内定期发布)。这些事件可以是用户输入事件或计时器事件。用户输入事件只是设置为对下一个计时器事件做出反应。 (当你没有任何事件来处理你的睡眠时)

您应该始终假设每个计时器事件都在同一时间处理,即使处理落后可能会有一些漂移。你可能会注意到的主要奇怪之处在于,如果游戏落后于处理计时器事件然后再次赶上,游戏中的时间可能会减慢(低于实时),然后加速(到实时),并且然后慢下来(到实时)。

处理此问题的方法包括仅允许一个计时器事件一次进入事件队列,这将导致时间显示为减速(低于实时),然后加速(实时)加速超速间隔。

另一种方法是在功能上类似于你所拥有的,处理每个计时器事件的最后一步是排队下一个计时器事件(请注意,没有其他人应该发送计时器事件{除了对于第一个}如果这是你选择实施游戏的方式)。这意味着取消计时器事件之间的常规时间间隔,并限制程序休眠的能力,因为至少每次检查事件队列时都会有一个计时器事件要处理。