代码之家  ›  专栏  ›  技术社区  ›  Nick Van Brunt

这是一个很好的实现FPS独立游戏循环?

  •  4
  • Nick Van Brunt  · 技术社区  · 15 年前

    我目前有一些接近于以下的物理游戏的FPS独立游戏循环的实现。它在我测试过的每一台电脑上都运行得很好,当帧速率下降时保持游戏速度一致。不过,我将要移植到嵌入式设备,这可能会与视频斗争更艰难,我想知道它是否仍将削减芥末。

    编辑:

    #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 回复  |  直到 15 年前
        1
  •  6
  •   nategoose    15 年前

    我有几点意见。首先是你没有足够的评论。有些地方不清楚你想做什么,所以很难说是否有更好的方法来做,但我会在我来的时候指出这些。不过,首先:

    #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;
    

    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 loop(外环)的意思是,但我不太清楚内环。另外,传递给 doStep 函数——这是什么意思。

    }
    

    这是最后一个大括号。我觉得它看起来很孤独。

    我不知道到底发生了什么

    您应该始终假设每个计时器事件都是在同一时间段处理的,尽管如果处理落后,这里可能会有一些偏移。这里你可能注意到的最奇怪的是,如果游戏在处理计时器事件上落后了,然后又赶上了,游戏中的时间可能看起来慢下来(低于实时),然后加速(到实时),然后慢下来(到实时)。

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