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) / 1000000is negative? That's easy, it means thatframeTime * 2 - updateTimeis negative, which means thatupdateTimeis greater thanframeTime * 2. ThereforecurrentTime - lastTimeis 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\$updateTimecould be larger thanframeTime * 2. Another process could be stealing more than that amount of time when you callThread.sleepin the previous frame, causing the currentupdateTimeto be unexpectedly large. \$\endgroup\$updateTimewill be nearly zero. This means you passThread.sleepalmostframeTime * 2. This makes the second calculation ofupdateTimeresult in aboutframeTime * 2, which means you passThread.sleepjust 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 leastframeTimeto 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\$