Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have a FPS function that:

private static void runGameFPS2() {
    long beginTime; //The time when the cycle begun
    long timeDiff; //The time it took for the cycle to execute
    int sleepTime; //ms to sleep (< 0 if we're behind)
    int fps = 1000 / 40;

    beginTime = System.currentTimeMillis();

    //Update game state
    updateGame();
    //Render state to the screen
    renderScreen();

    //Calculate how long did the cycle take
    timeDiff = System.currentTimeMillis() - beginTime;

    //Calculate sleep time
    sleepTime = fps - (int)(timeDiff);

    if (sleepTime > 0) {//If sleepTime > 0 we're OK
        try { Thread.sleep(sleepTime); }catch(InterruptedException e){}
    }else{
        //suppose will not be
    }
}

public static void main(String[] args){
    while(true){ runGameFPS2(); }
}

  When I run my code, Java works at different speeds on same computer or on different computers.

How can I write a consistent code, that Java will always work "same time" (for example 40 FPS) on mycomputer or any computer?

Note: suppose sleepTime will never be sleepTime < 0

share|improve this question

4 Answers 4

up vote 5 down vote accepted

System.currentTimeMillis() isn't a very good method to use for this sort of timing as it only has a resolution of 16ms on Windows based systems.

Try using System.nanoTime() / 1000000 instead, and see if that makes it better.

You could also switch to using nanoseconds entirely, there is a Thread.sleep(long, int) that takes nanoseconds.

share|improve this answer
    
I guess nanoTime is better than currentTimeMillis, now i changed currentTimeMillis to nanoTime, it is working better for now but i must try it on all computers. –  Kumul Jul 26 '12 at 8:11
    
It's not that the timer has a resolution of 16ms intrinsically - this is configurable via calls to the Windows API, specifically the timeBeginPeriod function. –  Kylotan Jul 26 '12 at 15:58
    
Yes, although if you end up needing to use timeBeginPeriod you're probably doing something wrong. If you need high precision timers, you really should be using nanoTime/QueryPerformanceCounter. –  elFarto Jul 26 '12 at 16:55

Your problem might be that you're calling sleep() on each loop. I don't think sleep() is high precision, meaning it may deal in 16ms increments on Windows also, like System.currentTimeMillis().

Your best bet is not to sleep in the main loop. Try just running it without sleeping and see if the timing is still inconsistent.

EDIT:

You control the speed by calculating how far the box has moved during the frame. The basic formula is distance = speed * time, so the speed of the object multiplied by the time between frames gives you the object's change in position between frames.

For example, if you want the box to move 100 pixels per second, then do a calculation like:

// get time since last frame
currentFrameTime = System.currentTimeMillis();
timeBetweenFrames = currentFrameTime - lastFrameTime;
lastFrameTime = currentFrameTime;

// calculate distance change and new position
distanceX = 100 * (timeBetweenFrames / 1000); 
box.x += distanceX;

Just be sure to get your units the same (that's why we divided timeBetweenFrames by 1000, since it's in milliseconds).

share|improve this answer
    
+1, almost like you read my mind! –  Darth Satan Jul 25 '12 at 16:25
    
If i don't use sleep in main, for example i draw a box and it is moving "box.x += 5;" and if i don't use sleep, the human eye can't see the box during its moving? or am i understand wrong? how can i control the speed of game? (my english is poor, sorry for bad sentences.) –  Kumul Jul 25 '12 at 20:35
    
@Kumul I've edited my answer to try to answer your question. –  Steve Blackwell Jul 25 '12 at 21:11
    
I did not implement your code to my function exactly :/ –  Kumul Jul 26 '12 at 8:42
    
The edit describes a better way to do things than artificially slowing your game loop. Consider what happens when your FPS drops below 40 on a slower machine or when there are more entities to update/draw. –  Eric Jul 26 '12 at 15:27

Sleep itself is not a good method to use for anything in a game loop, and is even worse for use as an FPS limiter - the only realistic use case for it is if you have an absolute power-saving requirement that is considered more important than timer accuracy.

Reason why is that Sleep fails on two counts. First it is a low-precision timer itself, using (again) an approx. 16ms resolution. Second it only guarantees a minimum time to sleep for - the actual time it does sleep for can be any arbitrary time-span above this.

If you want to limit frame rate, do so based on accumulated time or on vsync, but never use any Sleep call for this purpose.

share|improve this answer
    
All correct, except it's not that Sleep() has a resolution of 16ms intrinsically - this is configurable via calls to the Windows API, specifically the timeBeginPeriod function. –  Kylotan Jul 26 '12 at 15:59

Sleeping can't promise a precise come back. That is handled by the scheduler of the JVM as well as the Operative System. It's impossible to accomplish it that way. You must accept the fact you can't control it and compensate for over sleeping as well.

The most complete way to make it work is by this piece of code extracted from the book "Killer Game Programming in Java". It does all this:

  • Makes sure that you work at the desired frame rate consistently across any computer.
  • Divides updating the game state from actually rendering it
  • Releases the current thread if it finished updating and rendering the frame in less than the required time. It also releases the current thread in case it is hogging the CPU because it didn't have extra time.
  • Handles the case where it overslept and tries to catch up
  • In case it takes longer to make update and render for a given frame it takes note. Then it will skip the rendering but will do an update. If too many renderings are being skipped it will perform a rendering even if it still didn't catch up so the image doesn't freeze.

It's the most solid update/render loop I have come across in several years.

// Desired frames per second
private static int FPS = 60;
// nanoseconds per frame
private long period = 1000000000L / FPS;
private boolean running = true;
// no. of frames that can be skipped in any one animation loop
// i.e the games state is updated but not rendered
private static int MAX_FRAME_SKIPS = 5;
/**
 * Number of frames with a delay of 0 ms before the animation thread yields to other running threads.
 */
private static final int NO_DELAYS_PER_YIELD = 16;

/*
 * Repeatedly update, render, sleep so loop takes close to period nsecs. Sleep inaccuracies are handled.
 *
 * Overruns in update/renders will cause extra updates to be carried out so UPS tild== requested FPS
 */
public void run() {
    long beforeTime, afterTime, timeDiff, sleepTime;
    long overSleepTime = 0L;
    int noDelays = 0;
    long excess = 0L;

    beforeTime = System.nanoTime();

    running = true;
    while (running) {
        gameUpdate();
        gameRender();
        paintScreen();

        afterTime = System.nanoTime();
        timeDiff = afterTime - beforeTime;
        sleepTime = (period - timeDiff) - overSleepTime;

        if (sleepTime > 0) {   // some time left in this cycle
            try {
                Thread.sleep(sleepTime / 1000000L);  // nano -> ms
            } catch (InterruptedException ex) {
            }
            overSleepTime = (System.nanoTime() - afterTime) - sleepTime;
        } else {    // sleepTime <= 0; frame took longer than the period
            excess -= sleepTime;  // store excess time value
            overSleepTime = 0L;

            if (++noDelays >= NO_DELAYS_PER_YIELD) {
                Thread.yield();   // give another thread a chance to run
                noDelays = 0;
            }
        }

        beforeTime = System.nanoTime();

        /*
         * If frame animation is taking too long, update the game state without rendering it, to get the updates/sec
         * nearer to the required FPS.
         */
        int skips = 0;
        while ((excess > period) && (skips < MAX_FRAME_SKIPS)) {
            excess -= period;
            gameUpdate();      // update state but don't render
            skips++;
        }
    }

    System.exit(0);
}
share|improve this answer
    
Do not you have to use "System.nanoTime() / 1000000" for each nanoTime line? because you are calculating "sleepTime = (period - timeDiff) - overSleepTime;" the "period" is "Millisecond" and other variables is nanotime? –  Kumul Jul 26 '12 at 8:58
    
You are right, period is wrong there. It should be defined as time in nanoseconds, not milliseconds. Everything should be in nanos so it's more precise. I fixed it now. When dealing with 60 frames per second you are talking about 16.666... ms. If you store that in an int or long you end up with 16 and thus 62.5 frames per second or 17 if you round it and thus 58.8 frames per second. –  Tomas Lazaro Jul 26 '12 at 14:28

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.