Java游戏循环中的确切时间

时间:2015-03-14 18:35:51

标签: java network-programming timing game-loop

我正在开发基于网络的游戏,现在我正专注于服务器端模拟。当然我需要一个游戏循环,并且我选择了一个固定的时间步长循环,这样它在客户端上的再现比变量时间步长更容易。我还决定以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帧),但应该能够永远不会慢于所需的速度。

提前致谢。

1 个答案:

答案 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,即使它不应该有任何显着的差异