我正在进行游戏,并且我正在实施 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;
}
}
}
正如您所看到的那样,不准确,有时它会低于60,有时会高一些!我的PC硬件有时无法达到60的问题,因为解锁后我得到1700-2000 FPS。我希望它尽可能精确。
提前致谢。
答案 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>