Millad
Millad

Reputation: 1595

Java's currentTimeMillis returns negative value(s)

I'm developing a simple 2D game in Java, everything works fine. To find the correct FPS refresh/redraw/update, I used currentTimeMillis to find the difference.

The problem is that currentTimeMillis sometimes returns negative values, and the Thread.sleep will throw exception (java.lang.IllegalArgumentException: timeout value is negative)

What I did was to put a while in my game and while currentTimeMillis <= -1 check again until its over, then sleep.

Code sample:

private void gameLoop(){
    // Find FPS
    long FPS = 40;
    long beginTime = System.currentTimeMillis();
    while(beginTime < -1){
        beginTime = System.currentTimeMillis();
    }
    while(!done){
        // Sleep
        try{ 
            beginTime += FPS;
            long currTime = System.currentTimeMillis();
            while(currTime < -1){
                currTime = System.currentTimeMillis();
            }
            difference = (beginTime - currTime);
                           // Should be (currTime - beginTime) 

            Thread.sleep(difference); 
        }catch (Exception e){ 
            e.printStackTrace();
        }
        // RENDER GAME
        renderGame();
    }
    JOptionPane.showMessageDialog(null, "Game Over\nYou Died.");
    System.exit(0);
}// end gameLoop()

When the game starts, this works fine, but sometimes I still get the Exception. Is there a better way? I still think it´s strange that it´s returning a negative value.

Upvotes: 2

Views: 9343

Answers (7)

Andreas Dolk
Andreas Dolk

Reputation: 114837

There is a conceptual problem in your code: at one point you add a framerate (Frames per second, FPS) to a time. Or you really want to add 40 milliseconds, but then you shouldn't name the variable FPS because this will lead to misunderstandings.

Assuming FPS is really frames per second, then you should use another value for your calculation:

long milliSecondsPerFrame = 1000 / FPS;

System.getCurrentTimeInMillis() will never return negative values (or at least not before Aug, 17, 08:12:55 - in the year 292.278.944 ;) ) *)

So you can safely remove the checks for negative currentTimeMillis() for real time usages.

The name of the beginTime variable leads to another misunderstanding. This is not the timestamp of some kind of beginning. In fact its the time when you expect the next frame update. So you should name it different, maybe 'nextFrameUpdateTime'. That would make clear, that the calculation of the difference is actually

difference = nextFrameUpdateTime - currentTime;

is correct and should return a positive value - as long as your sure, that you didn't miss a frame update ('you're too late'). And I think, that's the problem: a negative difference just indicates, that the renderGame() method took more time than the secondPerFrame value, so at some point during your game you just missed an update, the currentTime is bigger then the proposed nextFrameUpdateTime and you have the exception.

*) Tested with

Date date = new Date(Long.MAX_VALUE);

Upvotes: 1

CodeGoat
CodeGoat

Reputation: 1196

If you're trying to set a specific frame rate, here's what it should look like:

long msPerFrame = 1000 / 60;
long now = System.currentTimeMillis();
long next = now + msPerFrame;
while(!done)
{
    now = System.currentTimeMillis();
    if(now >= next)
    {
        next = now + msPerFrame;
        renderGame();
    }
    else
    {
        // no chance of negative, we know next > now
        long diff = next - now;
        try { Thread.sleep(diff); } catch(InterruptedException ex) { }
    }
}

This is just a "close enough" fixed rate game loop, and should give you the basic idea. I say close enough because if renderGame() takes longer than msPerFrame, the game will slow down (like NES games when there was too much on the screen).

There's better ways to run a game loop, like using frame rate independent motion, or using frame skipping to handle slowdowns, but this is a good way to do it for a first game.

Upvotes: -1

Millad
Millad

Reputation: 1595

It seems like this worked better.

private void gameLoop(){
    // Find FPS
    long nextFrameUpdateTime = System.currentTimeMillis();
    long milliSecondsPerFrame = 1000 / 60;
    while(!done){
        // Sleep
        try{ 
            long currentTime = System.currentTimeMillis();
            currentTime += milliSecondsPerFrame;

            difference = ( currentTime - nextFrameUpdateTime );
            Thread.sleep(difference); 

        }catch (Exception e){ e.printStackTrace(); }

        nextFrameUpdateTime = System.currentTimeMillis();
        renderGame();

    } // end While
    JOptionPane.showMessageDialog(null, "Game Over\nYou Died.");
    System.exit(0);
}// end gameLoop()

Thank you for your help everyone.

Upvotes: 0

Andreas Petersson
Andreas Petersson

Reputation: 16518

for calculation of fps values i would recommend using

System.nanoTime()

it is way more accurate. the drawback is it gets you only realtive values, which does not matter at all when doing fps calcuation.

1 millisecond consists of 1000000 nanoseconds.

the resolution of currentTimeMillis is usually only 16ms.

Upvotes: 0

Brian Agnew
Brian Agnew

Reputation: 272417

Isn't the real problem that beginTime-currTime is negative ?

difference = (beginTime - currTime);
Thread.sleep(difference); 

I note that you add FPS (40) to beginTime. But if currentTime is greater than beginTime+FPS, you're going to have problems.

If you're trying to schedule something at regular intervals (which I think you are), check out Timer, which will allow you to do this more simply/reliably.

Upvotes: 11

dfa
dfa

Reputation: 116442

imho the following instruction:

difference = (beginTime - currTime);

should be:

difference = (currTime - beginTime);

currTime is always greater than beginTime, so difference is always negative or at least 0.

Upvotes: 5

stepancheg
stepancheg

Reputation: 4276

If currentTimeMillis returns negative value, then something is wrong with your JVM/OS, or memory corrupted etc.

Upvotes: 1

Related Questions