我正在开发基于网络的游戏,现在我正专注于服务器端模拟。当然我需要一个游戏循环,并且我选择了一个固定的时间步长循环,这样它在客户端上的再现比变量时间步长更容易。我还决定以60赫兹的速度运行游戏。这是游戏逻辑速度,而不是渲染速度。将使用客户端中的可变时间步循环来处理渲染,以获得最佳渲染。 服务器是用Java编写的。
我已经使用http://www.java-gaming.org/index.php?topic=24220.0中的代码制作了一个示例游戏循环,并使用我的代码修改了循环。这是循环:
private void gameLoop()
{
final double GAME_HERTZ = 60.0;
final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
//We will need the last update time.
double lastUpdateTime = System.nanoTime();
//Store the last time we rendered.
double lastRenderTime = System.nanoTime();
int lastSecondTime = (int) (lastUpdateTime / 1000000000);
long extraSleepTime = 0;
while (running)
{
int updateCount = 0;
if (!paused)
{
long loopStartTime = System.nanoTime();
updateGame();
updateCount++;
long timeAfterUpdate = System.nanoTime();
lastUpdateTime = timeAfterUpdate;
//Render. To do so, we need to calculate interpolation for a smooth render.
float interpolation = Math.min(1.0f, (float) ((loopStartTime - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
drawGame(interpolation);
lastRenderTime = loopStartTime;
//Update the frames we got.
int thisSecond = (int) (lastUpdateTime / 1000000000);
if (thisSecond > lastSecondTime)
{
long nanoTime = System.nanoTime();
System.out.println("NEW SECOND " + thisSecond + " " + frameCount + ": " + (nanoTime - lastNanoTime));
lastNanoTime = nanoTime;
fps = frameCount;
frameCount = 0;
lastSecondTime = thisSecond;
}
long loopExecutionTime = timeAfterUpdate - loopStartTime;
long sleepTime = (long)TIME_BETWEEN_UPDATES - loopExecutionTime - extraSleepTime;
// Only sleep for positive intervals
if(sleepTime >= 0)
{
try
{
Thread.sleep(sleepTime / 1000000);
}
catch(InterruptedException e) {}
}
else
{
System.out.println("WARN: sleepTime < 0");
}
// Counts the extra time that elapsed
extraSleepTime = System.nanoTime() - timeAfterUpdate - sleepTime;
}
}
问题在于,在运行时,FPS在60Hz时不稳定,但有时会降低。例如,我有时会得到58-59Hz,低至57Hz。 如果游戏在本地运行,这种可变性不会成为问题,但是当我们的游戏联网时,我需要保留准确的时间,以便我可以在客户端和服务器上重现逻辑计算。
此代码中是否有任何错误,或者可以改进哪些内容以使其更稳定?我们的目标是始终保持60Hz。
编辑:我想到的第一个解决方案是运行循环比它需要的速度快一些,例如70Hz,并检查帧数以将更新限制为每秒60。这样,模拟将以突发方式运行,并且需要缓冲(一次最多60帧),但应该能够永远不会慢于所需的速度。
提前致谢。
答案 0 :(得分:0)
如果你想达到每秒60帧,你最好使用预定的执行器,因为Thread.sleep()
可能没有你想要的那么精确。请考虑以下服务器代码示例:(请注意它包含Java 8代码)
public void gameLoop() {
// game logic here
}
Executors.newSingleThreadScheduledExecutor()
.scheduleAtFixedRate(this::gameLoop, 0, 16, TimeUnit.MILLISECONDS)
它将每16毫秒运行一次gameLoop()
,这基本上就是你想要的。这应该会给你更精确的结果。你也可以用他们的纳秒级替换16和TimeUnit.MILLISECONDS
,即使它不应该有任何显着的差异