Synchronisation exacte dans la boucle de jeu Java


Je développe un jeu basé sur le réseau, et je me concentre maintenant sur la simulation côté serveur. Bien sûr, j'ai besoin d'une boucle de jeu, et j'ai opté pour un fixed timestep boucle de sorte qu'il sera beaucoup plus facile à reproduire sur le client(s) qu'une variable timestep un. J'ai également décidé de lancer mon jeu à 60 Hz. C'est la vitesse logique du jeu, pas la vitesse de rendu. Le rendu sera géré avec une boucle timestep variable dans les clients pour avoir le meilleur rendu possible. Le serveur est écrit en Java.

Je déjà fait un exemple de boucle de jeu en utilisant le code de http://www.java-gaming.org/index.php?topic=24220.0 et en modifiant la boucle avec mon code. Voici la boucle:

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;
     }
  }

Le problème est que, lors de l'exécution, les FPS ne sont pas stables à 60Hz, mais vont parfois plus bas. Par exemple, j'obtiens parfois 58-59Hz, allant aussi bas que 57Hz. Cette variabilité ne serait pas un problème si le jeu était exécuté localement, mais comme notre jeu est en réseau, je dois garder l'heure exacte afin de pouvoir reproduire la logique calculs sur le client et le serveur.

Y a - t-il une erreur dans ce code, ou quelque chose qui pourrait être amélioré pour le rendre plus stable? Notre but est 60Hz étant gardé exactement tout le temps.

EDIT: Une première solution qui m'est venue à l'esprit est d'exécuter la boucle un peu plus vite qu'il n'en a besoin, par exemple à 70Hz, et de vérifier le nombre d'images pour limiter les mises à jour à 60 par seconde. De cette façon, la simulation serait exécutée en rafales et nécessiterait une mise en mémoire tampon (jusqu'à 60 images à la fois), mais devrait pouvoir ne jamais être plus lent que nécessaire.

Merci d'avance.

Author: Iamsomeone, 2015-03-14

1 answers

, Si vous voulez atteindre 60 images par seconde, vous feriez mieux d'utiliser un planifiée comme exécuteur testamentaire Thread.sleep() ne peut pas être aussi précis que vous le souhaitez. Considérez l'exemple suivant pour votre code de serveur: (Veuillez noter qu'il contient du code Java 8)

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

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

Il exécutera votre gameLoop() toutes les 16 millisecondes, ce qui est essentiellement ce que vous voulez. Cela devrait vous donner des résultats beaucoup plus précis. Vous pouvez également remplacer 16 et {[3] } par leur homologue nanosecondes, même si cela ne devrait pas en faire différence notable

 0
Author: AlmasB, 2015-05-05 20:33:09