Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

running a while loop on an interval

Tags:

java

I am building a game with a good emphasis on physics. Therefore I need the game to run on a very specific interval. Current code:

public double period = .02; //this is the run interval in seconds

//main gameLoop
public void gameLoop(){
    long startTime;
    long sleep;

    while(running){
        startTime = System.nanoTime();

        Graphics2D g = s.getGraphics();
        operateEntities(g);
        g.dispose();
        s.update();
        //figure out how long it must sleep to take .02s altogether
        sleep = ((int)(period*1000) - (System.nanoTime() - startTime)*100000);
        try{
            if(sleep > 0){
                Thread.sleep(sleep);
            }else{
                System.err.println("Warning: program runtime exceeded period");
            }
        }catch(Exception ex){}

        gameTime += period;
    }
}

This is not working as expected. Currently the main thread is executing without sleeping at all, and the "Warning: program runtime exceeded period" warning is firing.

Previously I was using System.currentTimeMillis(), but it was not accurate enough for my purposes, so I switched to System.nanoTime()

Increasing the period actually serves to speed up the program, while reducing it slows it down.

Is there a simple logic faw? is my understanding of System.nanoTime() off? or is there a better way to run the methods operateEntities, dispose, and update on a specific interval?

EDIT: for the record, the program does not take more than .02s to complete. It has been tested

like image 542
Spencer Allen Avatar asked Mar 04 '26 06:03

Spencer Allen


2 Answers

Nanoseconds are smaller than milliseconds, and as such, to convert nanos -> millis, you must divide by 100000, not mulitply by it;

    //figure out how long it must sleep to take .02s altogether
    sleep = ((int)(period*1000) - (System.nanoTime() - startTime)*100000);

should be changed to

    //figure out how long it must sleep to take .02s altogether
    sleep = ((int)(period*1000) - (System.nanoTime() - startTime)/100000);

Your current code was trying to sleep 200ms minus a big number, making sleep be negative, and giving you the "Warning: program runtime exceeded period" output

like image 166
Alex Coleman Avatar answered Mar 06 '26 19:03

Alex Coleman


Breaking down your code there are a number of problems:

//Multiplies by 100,000 rather than divides.
sleep = ((int)(period*1000) - (System.nanoTime() - startTime)*100000);
//Note that sleep here is a very small number minus a very large number: probably negative.

try{
  if(sleep > 0){//If positive, sleep
    Thread.sleep(sleep);
  } else{//throws an error in all other cases.
    System.err.println("Warning: program runtime exceeded period");
  }
}catch(Exception ex){}//Empty exception handling poorly handles the thread.sleep() Exception requirement.

This code will always error out unless you make your period value much larger. However, even beyond that your approach will not likely yield the result you want: accurate timing. What your core loop is:

  • Calculate the physics for 0.02 seconds.
  • Go to sleep.
  • Check what time it is.
  • If a particular period (0.02 seconds) has passed, continue, otherwise sleep again.
  • Repeat.

With a small enough time slice this will be accurate. However, threads don't work like that. You cannot guarantee when the thread will wake up. It could be never. It could be in three seconds. It could be instantly. Chances are you're going to overshoot whatever your time period is, and you will effectively never hit it dead on.

Instead of relying on a specific incremental period, you need to scale all your physics by the period of time that has actually passed, rather than rely on a specific period of time passing consistently every time.

  • Go to sleep.
  • Find out how much time has passed.
  • Calculate physics for that time period.
  • Repeat.

You still want a small time slice to sleep, but this way you eliminate the error introduced by the thread scheduler.

like image 38
Nathaniel Ford Avatar answered Mar 06 '26 20:03

Nathaniel Ford