Reputation: 1595
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
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
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
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
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
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
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
Reputation: 4276
If currentTimeMillis returns negative value, then something is wrong with your JVM/OS, or memory corrupted etc.
Upvotes: 1