David Kenz David Kenz - 5 months ago 15
Java Question

FPS lock not precise

I'm making a game and I'm implementing the FPS cap. But it isn't really working as it isn't much precise.

This is the code I have:

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


It should be capping the FPS at 60 FPS but instead the result is:
enter image description here

As you can see it's really not accurate, sometimes it's a lot lower than 60 and sometimes a bit higher! And problem of sometimes not reaching 60 isn't of my PC hardware since when unlocked I get 1700-2000 FPS. I want it to be as precise as possible.

Thanks in advance.

Answer

First of all I see you mixed System.currentTimeMilis() and System.nanoTime() not really a good idea, use only either one of them. Better only use System.nanoTime() since you are working on a high precision.

What's causing your issue is Thread.sleep() is not precise enough. So you need to avoid sleeping. Change your sleeping code to this;

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();
    }
}

About how to enable VSYNC, your application need to be full screen and you should call Toolkit.sync() after every render.

Comments