5

我目前对基于物理的游戏的 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
        }
    }
}
4

1 回答 1

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

您应该始终假设每个计时器事件是在同一时期处理的,即使如果处理落后,这里可能会有一些偏差。您可能会注意到这里的主要奇怪之处是,如果游戏在处理计时器事件时落后,然后又赶上来,那么游戏中的时间可能会变慢(低于实时),然后加快(到实时),并且然后放慢速度(到实时)。

处理这个问题的方法包括一次只允许一个计时器事件在事件队列中,这将导致时间看起来变慢(低于实时)然后加速(到实时)而没有超速间隔.

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

于 2010-10-18T19:44:26.490 回答