我正在用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()
当游戏开始时,这样可以正常工作,但有时我仍然会得到异常。有没有更好的办法?我仍然认为它返回一个负值很奇怪。
答案 0 :(得分:11)
beginTime-currTime
不是真正的问题吗?
difference = (beginTime - currTime);
Thread.sleep(difference);
我注意到您将FPS
(40)添加到beginTime
。但如果currentTime
大于beginTime+FPS
,您就会遇到问题。
如果您正在尝试定期安排某些事情(我认为您是这样),请查看Timer,这样您就可以更简单/更可靠地执行此操作。
答案 1 :(得分:5)
imho以下指示:
difference = (beginTime - currTime);
应该是:
difference = (currTime - beginTime);
currTime始终大于beginTime,因此差异总是负数或至少为0.
答案 2 :(得分:1)
如果currentTimeMillis返回负值,那么JVM / OS出现问题或内存损坏等。
答案 3 :(得分:1)
您的代码中存在一个概念性问题:您可以在某个时刻添加帧速率(每秒帧数,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);
答案 4 :(得分:0)
用于计算fps值我建议使用
System.nanoTime()
它更准确。缺点是它只能获得实际值,这在进行fps计算时根本不重要。
1毫秒由1000000纳秒组成。
currentTimeMillis的分辨率通常只有16ms。
答案 5 :(得分:0)
看起来效果更好。
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()
感谢大家的帮助。
答案 6 :(得分:-1)
如果您尝试设置特定的帧速率,请按以下步骤显示:
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游戏一样)。
有更好的方法来运行游戏循环,例如使用帧速率独立运动,或使用跳帧来处理减速,但这是第一场比赛的好方法。