I've been working on a Java game, but I couldn't keep it from stuttering. I did some research, and found out that I was using a bad game loop. I've been working on a new game loop, which works a little better, but my framerate often jumps from 60 FPS to 62 FPS, and sometimes even crashes. This is the loop I'm using;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
@SuppressWarnings("serial")
public class GameLoop extends Applet implements Runnable {
final int FRAMERATE = 60;
@Override
public void init() {
setSize(1280, 720);
setBackground(Color.WHITE);
setFocusable(true);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("Game Loop");
}
@Override
public void start() {
Thread thread = new Thread(this);
thread.start();
}
@Override
public void stop() {
}
@Override
public void destroy() {
}
@Override
public void run() {
int framerate = 0;
long counter = 0;
long lastTime = System.nanoTime();
long frameTime = 1000000000 / FRAMERATE;
while (true) {
long currentTime = System.nanoTime();
long updateTime = currentTime - lastTime;
lastTime = currentTime;
counter += updateTime;
framerate++;
if (counter >= 1000000000) {
System.out.println(framerate + " fps");
counter = 0;
framerate = 0;
}
try {
Thread.sleep((frameTime * 2 - updateTime) / 1000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I've walked it through a couple of times, but I can't find out what's wrong. This is the console output I get;
60 fps
60 fps
60 fps
60 fps
62 fps
60 fps
62 fps
60 fps
60 fps
60 fps
...
Exception in thread "Thread-3" java.lang.IllegalArgumentException: timeout value is negative
at java.lang.Thread.sleep(Native Method)
at GameLoop.run(GameLoop.java:58)
at java.lang.Thread.run(Unknown Source)
As far as I can understand, the loop crashes because Thread.sleep((frameTime * 2 - updateTime) / 1000000);
has a negative input, but I don't know why it is negative.
I've added;
if (updateTime < 0)
updateTime = 0;
After;
long currentTime = System.nanoTime();
long updateTime = currentTime - lastTime;
lastTime = currentTime;
To prevent getting a negative input, but the problem still occurs. Also, I've tried to implement the loop into my game, but it's even laggier than before. The player seems to be shaking back and forth while moving.
Could anyone tell me what I'm doing wrong and how I can fix this?
(frameTime * 2 - updateTime) / 1000000
is negative? That's easy, it means thatframeTime * 2 - updateTime
is negative, which means thatupdateTime
is greater thanframeTime * 2
. ThereforecurrentTime - lastTime
is greater thanframeTime * 2
. That means that the iteration took more time thanframeTime * 2
. Why? Read about Thread Preemption. How to fix it? Compute the time to the next update and substract the current time, instead of working with the interval (the result should be dropped frames). Or just check if negative \$\endgroup\$updateTime
could be larger thanframeTime * 2
. Another process could be stealing more than that amount of time when you callThread.sleep
in the previous frame, causing the currentupdateTime
to be unexpectedly large. \$\endgroup\$updateTime
will be nearly zero. This means you passThread.sleep
almostframeTime * 2
. This makes the second calculation ofupdateTime
result in aboutframeTime * 2
, which means you passThread.sleep
just about0
. Your framerate will always oscillate this way while you use this logic. The only way it will stabilize is if your game update takes at leastframeTime
to run, and at that point you'd be too slow for your target FPS. \$\endgroup\$Thread.sleep(Math.Max((frameTime * 2 - updateTime), 0) / 1000000);
anyone? \$\endgroup\$