0

I am attempting to make a game loop that has an accurate timer. I know that TimeUnit uses thread.sleep(); which can vary by milliseconds. My question is simple:

Does this code make thread.sleep(); more accurate?

and further

Is there anything I can do to fix/make it better, or should I abandon it?

import java.util.concurrent.TimeUnit;

public class Timer implements Runnable {

public static void main(String[]args){
    new Timer().start();
}

public int ups = 1;

@Override
public void run() {
    int uc = 0;
    long startTime = 0;
    long result = 0;
    long offset = 0;
    while(true){
        startTime = System.nanoTime();
        uc++;
        if(uc==1000/ups){
            update();
            uc=0;
        }
        result = System.nanoTime()-startTime;
        if(1000000-result>0)
            try {
                int prefered = 1000000;
                startTime = System.nanoTime();
                TimeUnit.NANOSECONDS.sleep(prefered-result-offset);
                offset = System.nanoTime()-startTime-prefered+result+offset;
            } catch (InterruptedException e) {
                System.out.println("an error has occured");
            }
    }
}

public void update(){
    System.out.println("Hello World");
}

public void start(){
    new Thread(this).start();
}
}

I have already seen this by the way. I might as well add

How does this compare to the other loop?

Edit: This is for a desktop game, not mobile

Community
  • 1
  • 1
cat16
  • 33
  • 9
  • 1
    Standard Java is not going to work for microsecond-level real-time precision without external (i.e. native library) help, if ever. – Jim Garrison Dec 06 '16 at 23:11
  • @Jim Garrison Well then do you have any suggestions? – cat16 Dec 06 '16 at 23:12
  • Depends completely on your environment -- embedded, desktop, Pi, etc. If you need microsecond precision you probably have to write in something closer to the hardware that compiles to machine instructions (i.e. not interpreted bytecodes) such as C. – Jim Garrison Dec 06 '16 at 23:14
  • I don't need nanosecond precision, as long as it works for a desktop game. Probably should add that to the question. – cat16 Dec 06 '16 at 23:15
  • Due to bytecode interpretation, JIT compilation and garbage collection overhead Java performance will always be variable and have unexpected dips. If you cannot tolerate occasional lags of dozens to hundreds of milliseconds then you cannot use Java. – Jim Garrison Dec 06 '16 at 23:17
  • As Garrison commented, such precision requires a special [Real-Time implementation of Java](https://en.m.wikipedia.org/wiki/Real_time_Java) rather than a conventional implementation such as OpenJDK. – Basil Bourque Dec 06 '16 at 23:24
  • @cat16 if you're looking for a game timer either [`javax.swing.Timer`](https://docs.oracle.com/javase/8/docs/api/javax/swing/Timer.html) or [`java.util.Timer`](https://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) should be fine – Whymarrh Dec 06 '16 at 23:25
  • @Whymarrh I am willing to do that, however do you have any reasons to use that instead of my current method? (maybe a link to another question) – cat16 Dec 06 '16 at 23:28
  • Write your game loop to run as fast as possible. Each time through the loop, read `System.nanoTime()` to get the elapsed time. That said, there are a lot of libraries that will help you with Java (1: https://www.lwjgl.org/ ) (2: https://libgdx.badlogicgames.com/ ) – markspace Dec 06 '16 at 23:29
  • @cat16 no reason in particular. Those classes are built into the stdlib and I imagine they'll work just as well as your implementation. – Whymarrh Dec 06 '16 at 23:30
  • @libgdx.badlogicgames.com But if it runs as fast as possible it kills the cpu... However I will take a look at lwjgl - I have heard a little about it. – cat16 Dec 06 '16 at 23:31
  • If you're writing a real time game, you have to kill the CPU to get performance. (Try running any modern game like Overwatch and another app at the same time. Neither will be happy.) OTOH if you're not writing a real time game then some other method (event driven?) is preferable to `Thread.sleep()` – markspace Dec 06 '16 at 23:33

1 Answers1

0

In case of games, I doubt you really want nanosecond precision in waking up. What you do want though is avoiding accumulating error, which is what your code is trying to do. It's a reasonable approach, although it probably would make more sense to use a Timer instead:

Timer timer = new Timer()
timer.scheduleAtFixedRate(new TimerTask() {
  @Override
  public void run() {
    // Your periodic code here
  }
}, 0, 1);
Roberto Attias
  • 1,883
  • 1
  • 11
  • 21