Programmation de jeux Java 2D: Différentes approches pour créer une boucle de jeu

10

Je suis nouveau dans la programmation de jeux Java, mais plus je lis, plus je suis confus, car j'ai vu plusieurs approches différentes pour faire une boucle de jeu: 1. L'approche standard, qui utilise la classe Timer (semble être moins précis). 2. L'approche plus précise qui utilise System.nanoTime. 3. Une approche simple qui utilise ScheduleAtFixedRate.

Laquelle devrait généralement être préférée et quels sont les avantages / inconvénients de chaque approche? Merci d'avance pour toute information.

user1812379
la source

Réponses:

7

Pour une boucle de jeu de base, vous exécutez une boucle while, dans laquelle vous obtenez le temps à l'aide de nanoTime (), déterminez le temps écoulé depuis la dernière image, puis mettez à jour votre état de jeu et le rendu.

À l'aide de http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/System.html#nanoTime%28%29 , vous pouvez interroger le temps écoulé. Fondamentalement...

public static void main(String [ ] args)
{
    long current_frame_time = system.nanoTime();
    long last_frame_time = current_frame_time;

    while(gameIsRunning)
    {
        last_frame_time = current_frame_time;
        current_frame_time = system.nanoTime();

        long timeTaken = current_frame_time - last_frame_time;

        //update and render game here
    }
}

Cette méthode de base peut être améliorée, par exemple http://www.koonsolo.com/news/dewitters-gameloop/ et http://gafferongames.com/game-physics/fix-your-timestep/ .

Vous pouvez également créer une minuterie et définir cette minuterie pour exécuter votre mise à jour et le rendu toutes les X millisecondes. Mais, il y a des inconvénients à construire une gameloop comme ça.

Selon http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Timer.html : " Correspondant à chaque objet Timer est un thread d'arrière-plan unique qui est utilisé pour exécuter tous les minuteurs les tâches du minuteur doivent se terminer rapidement. Si une tâche du minuteur prend trop de temps, elle "monopolise" le fil d'exécution des tâches du minuteur. Cela peut, à son tour, retarder l'exécution des tâches suivantes, qui peuvent "se regrouper" et exécutez-les en succession rapide lorsque (et si) la tâche incriminée se termine enfin. "

Selon http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Timer.html#scheduleAtFixedRate%28java.util.TimerTask,%20java.util.Date,%20long%29 : " Dans une exécution à taux fixe, chaque exécution est planifiée par rapport au temps d'exécution planifié de l'exécution initiale. Si une exécution est retardée pour une raison quelconque (comme la récupération de place ou une autre activité en arrière-plan), deux exécutions ou plus se succéderont rapidement. "rattraper". À long terme, la fréquence d'exécution sera exactement l'inverse de la période spécifiée (en supposant que l'horloge système sous-jacente à Object.wait (long) est exacte). "

Étant donné que vous ne savez généralement pas à l'avance combien de temps prend une boucle de jeu, le réglage d'une minuterie pour exécuter votre boucle de jeu toutes les X millisecondes (en fonction de la fréquence d'images cible) pourrait conduire à quelques images groupées, qui s'exécuteraient chaque fois qu'une image est terminé au lieu de la planification d'un cadre. Lorsque cela se produit ... pourquoi utiliser une minuterie en premier lieu?

Maintenant, ne vous méprenez pas, le minuteur n'est pas une mauvaise classe, mais il est généralement mieux adapté aux petites tâches qui doivent être effectuées périodiquement ou à un certain moment, par exemple, un client de messagerie peut vouloir vérifier les nouveaux messages tous les 5 minutes, ou un compteur peut être décrémenté chaque seconde pour afficher un compte à rebours avant le début d'une course.

Exilyth
la source
La question concernait les boucles de jeu, donc les informations awt / swing sont hors sujet. Cependant, il semblait que ces informations étaient pertinentes en raison d'une ligne non pertinente et déroutante dans la question, j'ai donc supprimé cela.
jhocking
J'ai adapté ma réponse pour refléter les changements apportés à la question.
Exilyth
7

Je ne mettrais pas la boucle principale dans une minuterie. Je ferais plutôt une boucle «while» qui traite chaque image, puis j'utiliserais une sorte de fonctionnalité de chronométrage (cela ressemble à Java qui serait System.nanoTime) pour calculer le temps écoulé depuis la dernière image / dernière itération de la boucle.

Certains langages sont des exceptions ici (par exemple, JavaScript, ActionScript) parce que ces langages fonctionnent dans un environnement qui a une boucle principale implicite à laquelle se connecter (par exemple, le navigateur, Flash Player) mais cette exception ne s'applique pas à Java.

jhocking
la source