游戏以 60fps 的速度运行,但在减速后,它会以超快的速度运行几秒钟

Game runs at 60fps, but after a slowdown, it runs super fast for few seconds

我正在开发一个瓷砖基础游戏。大多数时候游戏 "respects" 60fps 的上限,但是当我故意将 fps 设置为 10fps 5 秒(通过调试)以测试游戏渲染和游戏逻辑工作分离时,发生的事情是在我之后将游戏 fps 设置为 60fps,几秒钟后游戏会忽略 "cap" 并以大约 10000fps 的速度运行,然后回到上限。所有这一切的原因是,我希望我的游戏以可变的 fps 运行,但在所有机器上都保持恒定的逻辑速度,但是我不明白的是为什么我的游戏大部分时间都遵守上限并且从不超过 60fps 但当发生减速或滞后峰值,游戏运行速度超快 1-2 秒,然后恢复正常。无论用户是 运行 一台糟糕的电脑还是游戏以 5fps 运行,我想要的是确保游戏永远不会超过我的硬上限。

我是否需要实施 "skipped frames" 系统之类的? 仅供参考,我的游戏逻辑很轻,但是渲染很重。

new Thread(){
        public void run(){
            long hintPoint = System.currentTimeMillis();
            long nexttickG = System.currentTimeMillis();
            long nexttickL = System.currentTimeMillis();
            long nexttickV = System.currentTimeMillis();
            long ticktimeV = 1000;
            long LogicCap = 60;
            long ticktimeL = 1000/LogicCap;
            short frames = 60;

            while(isRunning){

                if(Main.servbot){
                    try {
                        Thread.sleep(15);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                GLOBAL_FPS_CAP=(short)Utils.Clamp(GLOBAL_FPS_CAP,1, 333);

                short FramesCap = GLOBAL_FPS_CAP;
                long ticktimeG = 1000/FramesCap;

                if (System.currentTimeMillis()>nexttickG){
                    Render();
                    long elapsed = System.currentTimeMillis() - hintPoint;
                    frames=(short)((long)1000/(Math.max(elapsed,1)));
                    hintPoint = System.currentTimeMillis();
                    nexttickG+=ticktimeG;
                }

                if (System.currentTimeMillis()>nexttickL){
                    GameLoop();
                    nexttickL+=ticktimeL;
                }

                if (System.currentTimeMillis()>nexttickV){
                    GLOBAL_FPS_HINT = frames;

                    nexttickV+=ticktimeV;
                }

                Thread.yield();

            }

        }
    }.start();
}

这些改变有帮助吗? (见下面的代码,寻找 /*new*/ )。

推理:您是否在 运行() 方法中设置了断点? 您修改了哪个变量

当您修改 var(s) 以更改帧速率时,System.currentTimeMillis() 中经过了多少毫秒?似乎如果你需要 3 或 4(也许 12)秒来进行更改并恢复执行,你的 nextTickL 和 nextTickG 值将远远落后于 System.currentTimeMillis() 并且可能需要大量添加 += ticktimeG 到赶上系统时钟。

new Thread(){
    public void run(){
        long hintPoint = System.currentTimeMillis();
        long nexttickG = System.currentTimeMillis();
        long nexttickL = System.currentTimeMillis();
        long nexttickV = System.currentTimeMillis();
        long ticktimeV = 1000;
        long LogicCap = 60;
        long ticktimeL = 1000/LogicCap;
        short frames = 60;

        while(isRunning){

            if(Main.servbot){
                try {
                    Thread.sleep(15);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            GLOBAL_FPS_CAP=(short)Utils.Clamp(GLOBAL_FPS_CAP,1, 333);

            short FramesCap = GLOBAL_FPS_CAP;
            long ticktimeG = 1000/FramesCap;

            if (System.currentTimeMillis()>nexttickG){
                Render();
                long elapsed = System.currentTimeMillis() - hintPoint;
                frames=(short)((long)1000/(Math.max(elapsed,1)));
                hintPoint = System.currentTimeMillis();
                nexttickG+=ticktimeG;
   /*new*/      if( nexttickG < System.currentTimeMillis() ) {
   /*new*/         // major clock skew somehow, reset.
   /*new*/         nexttickG = System.currentTimeMillis() + ticktimeG;
   /*new*/      }
            }

            if (System.currentTimeMillis()>nexttickL){
                GameLoop();
                nexttickL+=ticktimeL;
   /*new*/      if( nexttickL < System.currentTimeMillis() ) {
   /*new*/         // major clock skew somehow, reset.
   /*new*/         nexttickL = System.currentTimeMillis() + ticktimeL;
   /*new*/      }
            }

            if (System.currentTimeMillis()>nexttickV){
                GLOBAL_FPS_HINT = frames;

                nexttickV+=ticktimeV;
            }

            Thread.yield();

        }

    }
}.start();

首先,如果可能的话,使用 .nanoTime() 而不是 .currentTimeMillis(),因为第二个更容易出错。

其次,考虑实施 DELTA-TIME 系统,其中 FPSGame-World Time 独立于 each-other

对于动画游戏[,

Delta-Time是正确的loop-pattern(基本上是程序化的、系统的,有时 game-world 数据的 混乱 动画),因为它是最简单的模式这避免了大多数 naive/simplistic animation-loopgame-loop 实现的 time-drift bugs

Here's an example of how to implement a Delta-Time game-loop in Java.

有关简单循环实现问题的完整解释,以及 Delta-Time 如何解决 time-drift 问题 (以及为什么使用它),看看 this article and this article,它详细解释了游戏循环的差异。