[英]Java's currentTimeMillis returns negative value(s)
我正在用Java开发一个简单的2D游戏,一切正常。 为了找到正确的FPS刷新/重绘/更新,我使用currentTimeMillis来查找差异。
问题是currentTimeMillis有时会返回负值,而Thread.sleep会抛出异常(java.lang.IllegalArgumentException:timeout值为负)
我做的是在我的游戏中放一段时间,而currentTimeMillis <= -1再次检查直到它结束,然后睡觉。
代码示例:
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()
当游戏开始时,这很好,但有时我仍然得到例外。 有没有更好的办法? 我仍然认为它返回负值是很奇怪的。
beginTime-currTime
不是真正的问题吗?
difference = (beginTime - currTime);
Thread.sleep(difference);
我注意到,您添加FPS
(40) beginTime
。 但如果currentTime
大于beginTime+FPS
,那么你就会遇到问题。
如果你试图定期安排一些事情(我认为你是这样),请查看Timer ,这样你就可以更简单/可靠地完成这项工作。
imho以下指示:
difference = (beginTime - currTime);
应该:
difference = (currTime - beginTime);
currTime总是大于beginTime,因此差异总是负数或至少为0。
如果currentTimeMillis返回负值,那么您的JVM / OS出现问题,或者内存已损坏等。
您的代码中存在一个概念性问题:您可以在某个时刻添加帧速率(每秒帧数,FPS)。 或者您真的想要添加40毫秒,但是您不应该将变量命名为FPS,因为这会导致误解。
假设FPS实际上是每秒帧数,那么您应该使用另一个值进行计算:
long milliSecondsPerFrame = 1000 / FPS;
System.getCurrentTimeInMillis()永远不会返回负值(或至少不会在8月17日,08:12:55之前 - 在292.278.944年;)*)
因此,您可以安全地删除负currentTimeMillis()的检查以获取实时使用情况。
beginTime变量的名称导致另一个误解。 这不是某种开始的时间戳。 事实上,它是您期望下一帧更新的时间。 所以你应该把它命名为不同的,也许是'nextFrameUpdateTime'。 这表明,差异的计算实际上就是这样
difference = nextFrameUpdateTime - currentTime;
是正确的,应该返回一个正值 - 只要你确定,你没有错过帧更新('你太迟了')。 而且我认为,这就是问题所在:负差异只是表明,renderGame()方法花费的时间比secondPerFrame值多,所以在游戏中的某个时刻,你只是错过了更新,currentTime比建议的nextFrameUpdateTime更大,你有例外。
*)经过测试
Date date = new Date(Long.MAX_VALUE);
为了计算fps值,我建议使用
System.nanoTime()
它更准确。 缺点是它只能获得实际值,这在进行fps计算时根本不重要。
1毫秒由1000000纳秒组成。
currentTimeMillis的分辨率通常只有16ms。
看起来这样效果更好。
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()
谢谢大家的帮助。
如果您正在尝试设置特定的帧速率,那么它应该是这样的:
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) { }
}
}
这只是一个“足够接近”的固定速率游戏循环,应该给你基本的想法。 我说得足够接近因为如果renderGame()花费的时间比msPerFrame长,那么游戏就会变慢(比如屏幕上有太多的NES游戏)。
有更好的方法来运行游戏循环,例如使用帧速率独立运动,或使用跳帧来处理减速,但这是第一个游戏的好方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.