FPS锁不精确

时间:2016-06-24 00:44:41

标签: java graphics2d frame-rate

我正在进行游戏,并且我正在实施 FPS上限。但它并没有真正起作用,因为它并不精确。

这是我的代码

public static volatile int FPS_CAP = 60;

@Override
public void run() {
    long lastTime = System.nanoTime();
    double amountOfTicks = 60.0;
    double ns = 1000000000 / amountOfTicks;
    double delta = 0;
    long timer = System.currentTimeMillis(), lastRender;
    while (running) {
        long now = System.nanoTime();
        delta += (now - lastTime) / ns;
        lastTime = now;
        while (delta >= 1) {
            tick();
            delta--;
        }
        lastRender = System.currentTimeMillis();
        draw.render();
        draw.fps++;

        if (FPS_CAP != -1) {
            try {
                int nsToSleep = (int) ((1000 / FPS_CAP) - (System.currentTimeMillis() - lastRender));

                if (nsToSleep > 1 / FPS_CAP) {
                    Thread.sleep(nsToSleep);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        if (System.currentTimeMillis() - timer > 1000) {
            timer += 1000;
            draw.lastFPS = draw.fps;
            draw.fps = 0;
            // updates = 0;
        }
    }
}

应该将FPS限制为60 FPS,而是,结果是: enter image description here

正如您所看到的那样,不准确,有时它会低于60,有时会高一些!我的PC硬件有时无法达到60的问题,因为解锁后我得到1700-2000 FPS。我希望它尽可能精确。

提前致谢。

3 个答案:

答案 0 :(得分:3)

Java不够精确,无法实现这种精确机制。所有渲染库实际上都依赖于C或C ++层来管理实时精度。

在您的情况下,最好的解决方法是避免使用Thread.sleep()

您可以依赖Timer事件来在TimerTask期间运行更新。你的游戏每秒钟的心跳大约是60次。

另一个解决方案,如果60fps对你有好处,就是在渲染你的屏幕之前等待VSync。大多数LCD屏幕为60或120fps。 VSync应由您的图形库(swing,JavaFX或其他)管理。

最后一个解决方案,您可以查看专用引擎的代码(如JMonkey)作为参考。

答案 1 :(得分:1)

首先,我看到你混合System.currentTimeMilis()System.nanoTime()并不是一个好主意,只使用其中一个。最好只使用System.nanoTime(),因为你的工作精度很高。

Thread.sleep()造成问题的原因不够准确。所以你需要避免睡觉。将您的睡眠代码更改为此;

lastRender = System.nanoTime(); //change it to nano instead milli
draw.render();
draw.fps++;

if (FPS_CAP > 0) {
    while ( now - lastRender < (1000000000 / FPS_CAP))
    {
        Thread.yield();

        //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
        //You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
        //FYI on some OS's this can cause pretty bad stuttering. 
        try {Thread.sleep(1);} catch(Exception e) {}

        now = System.nanoTime();
    }
}

关于如何启用VSYNC,您的应用程序需要全屏显示,并且您应该在每次渲染后调用Toolkit.sync()

答案 2 :(得分:1)

JavaFX基于pulse mechanism

  

脉冲是向JavaFX场景图指示它的事件   是时候同步场景图上元素的状态   与棱镜。脉冲以每秒60帧(fps)的最大值进行节流   并且只要动画在场景图上运行就会触发。甚至   当动画未运行时,会在某些内容中安排脉冲   场景图改变了。例如,如果按钮的位置是   改变了,预定了一个脉冲。

     

触发脉冲时,场景图上元素的状态为   同步到渲染层。脉冲使应用   开发人员一种异步处理事件的方法。这很重要   功能允许系统批量处理脉冲上的事件。

     

。 。

     

Glass Windowing Toolkit负责执行脉冲   事件。它使用高分辨率的本机定时器来制作   执行。

要了解脉冲机制的实现,最好研究JavaFX源代码。一些关键类是:

Timer.java WinTimer.java win/Timer.h win/Timer.cpp

以上链接适用于通用Timer类和窗口特定的定时器实现。 JavaFX源代码包括其他平台的实现,例如OS X,GTK,iOS,Android等。

某些实现(如OS X似乎)允许Timer实现的vsync同步,其他实现(如Windows似乎)不允许vsync同步。虽然,这些系统可能变得复杂,但我想有可能在某些情况下,可能通过硬件图形管道而不是通过计时器实现vsync同步。

Windows本机代码基于timeSetEvent调用。

默认情况下,JavaFX帧率的上限为60fps,尽管它是adjustable via undocumented properties

如果您没有使用JavaFX(并且它似乎没有),您仍然可以检查JavaFX source code并了解其实现,以防您想要移植任何概念或代码用于您的应用程序。您也可以通过将应用程序子类化为JavaFX Application类或创建JFXPanel以启动JavaFX工具包,然后基于AnimationTimer实现计时器回调,将JavaFX计时器机制变为非JavaFX应用程序。 / p>