2015-03-14 4 views
0

Я разрабатываю сетевую игру, и теперь я фокусируюсь на моделировании на стороне сервера. Конечно, мне нужен игровой цикл, и я выбрал фиксированный цикл timestep, так что будет намного проще воспроизводить на клиенте (-ах), чем переменная timestep. Я также решил запустить свою игру на частоте 60 Гц. Это скорость логической игры, а не скорость воспроизведения. Рендеринг будет обрабатываться с переменным циклом timestep в клиентах, чтобы иметь наилучший возможный рендеринг. Сервер написан на Java.Точное время в цикле игры 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 не являются стабильными при 60 Гц, но иногда идти ниже. Например, я иногда получаю 58-59 Гц, опустившись до 57 Гц. Эта изменчивость не была бы проблемой, если бы игра выполнялась локально, но по мере того как наша игра подключена к сети, мне нужно сохранить точное время, чтобы я мог воспроизвести логические вычисления как на клиенте, так и на сервере.

Есть ли ошибки в этом коде или что-нибудь, что можно улучшить, чтобы сделать его более стабильным? Наша цель - 60 Гц, которые хранятся точно все время.

EDIT: Первое решение, которое появилось у меня в голове, работает на частоте цикла, например, на частоте 70 Гц, и проверяет количество кадров для ограничения обновлений до 60 в секунду. Таким образом, симуляция будет выполняться в пакетах и ​​потребуется буферизация (до 60 кадров за раз), но должна быть способна никогда не быть медленнее, чем необходимо.

Заранее спасибо.

ответ

0

Если вы хотите достичь 60 кадров в секунду, вам будет лучше использовать запланированный исполнитель, так как Thread.sleep() может быть не таким точным, как вам бы хотелось. Рассмотрим пример для кода сервера: (Пожалуйста, обратите внимание, что содержит Java 8 код)

public void gameLoop() { 
     // game logic here 
    } 

    Executors.newSingleThreadScheduledExecutor() 
      .scheduleAtFixedRate(this::gameLoop, 0, 16, TimeUnit.MILLISECONDS) 

Он будет запускать gameLoop() каждые 16 миллисекунд, который является по существу то, что вы хотите. Это должно дать вам гораздо более точные результаты. Вы также можете заменить 16 и TimeUnit.MILLISECONDS своими наносекундными аналогами, хотя он не должен делать заметной разницы.

+0

С этим цикл будет работать немного быстрее, хотя 1000/60 - 16 666 ..., а не только 16. С код, который я разместил в вопросе, я думаю, что он будет адаптироваться по мере необходимости, чтобы отрегулировать задержку (например, сделать несколько кадров в 16 мс и несколько в 17 мс), не так ли? – Iamsomeone

+0

Да, вы правы в обоих случаях. Однако на практике весьма вероятно, что запланированный исполнитель будет более «стабильным», тогда как Thread.sleep может иметь разные результаты. По тому же вопросу вы могли бы сделать исполнителя более точным, имея 16666 (6) ... и изменив TimeUnit на наносекунды. – AlmasB

+0

В любом случае оба метода должны выполнять эту работу и не должны вас беспокоить. Я бы сказал, завершите шаблон игры первым и отпустите его. На практике вы не увидите разницы между сервером, работающим на 57 или 60 кадров в секунду. – AlmasB