Je commence actuellement à apprendre OpenGL à l'école et j'ai commencé à faire un jeu simple l'autre jour (seul, pas pour l'école). J'utilise freeglut et je le construis en C, donc pour ma boucle de jeu, je venais vraiment d'utiliser une fonction à laquelle j'ai fait passer glutIdleFunc
pour mettre à jour tous les dessins et la physique en un seul passage. C'était bien pour des animations simples que je ne me souciais pas trop de la fréquence d'images, mais comme le jeu est principalement basé sur la physique, je veux vraiment (avoir besoin) de déterminer la vitesse de mise à jour.
Donc, ma première tentative a été de passer ma fonction à glutIdleFunc
( myIdle()
) pour garder une trace du temps écoulé depuis l'appel précédent et mettre à jour la physique (et actuellement les graphiques) toutes les millisecondes. j'ai utilisétimeGetTime()
de le faire (en utilisant <windows.h>
). Et cela m'a fait penser, est-ce que l'utilisation de la fonction de veille est vraiment une bonne façon de parcourir la boucle du jeu?
Ma question est, quelle est une meilleure façon d'implémenter la boucle de jeu dans OpenGL? Dois-je éviter d'utiliser la fonction de veille?
Réponses:
La réponse simple est non, vous ne voulez pas utiliser le rappel glutIdleFunc dans un jeu qui a une sorte de simulation. La raison en est que cette fonction divorce l'animation et tire le code de la gestion des événements de fenêtre mais pas de manière asynchrone. En d'autres termes, la réception et la remise des événements de fenêtre bloquent le code (ou tout ce que vous mettez dans ce rappel), cela convient parfaitement pour une application interactive (interagir, puis répondre), mais pas pour un jeu où la physique ou l'état du jeu doit progresser indépendamment de l'interaction ou du temps de rendu.
Vous voulez découpler complètement la gestion des entrées, l'état du jeu et dessiner le code. Il existe une solution simple et propre à cela qui n'implique pas directement la bibliothèque graphique (c'est-à-dire qu'elle est portable et facile à visualiser); vous voulez que la boucle de jeu entière produise du temps et que la simulation consomme le temps produit (en morceaux). La clé est cependant d'intégrer ensuite la quantité de temps que votre simulation a consommée dans votre animation.
La meilleure explication et tutoriel que j'ai trouvé à ce sujet est Fix Your Timestep de Glenn Fiedler
Ce tutoriel a le traitement complet, cependant si vous n'avez pas de simulation physique réelle , vous pouvez ignorer la véritable intégration mais la boucle de base se résume toujours à (en pseudo-code verbeux):
En procédant ainsi, les blocages dans votre code de rendu, la gestion des entrées ou le système d'exploitation n'entraînent pas de retard dans l'état de votre jeu. Cette méthode est également portable et indépendante de la bibliothèque graphique.
GLUT est une belle bibliothèque mais elle est strictement pilotée par les événements. Vous enregistrez des rappels et lancez la boucle principale. Vous passez toujours le contrôle de votre boucle principale à l'aide de GLUT. Il existe des hacks pour le contourner , vous pouvez également simuler une boucle externe à l'aide de minuteries et autres, mais une autre bibliothèque est probablement une meilleure façon (plus facile) de s'y rendre. Il existe de nombreuses alternatives, en voici quelques-unes (avec une bonne documentation et des tutoriels rapides):
la source
Je pense que
glutIdleFunc
c'est très bien; c'est essentiellement ce que vous finiriez par faire à la main de toute façon, si vous ne l'utilisiez pas. Peu importe ce que vous allez avoir une boucle serrée qui n'est pas appelée dans un modèle fixe, et vous devez faire le choix si vous voulez le convertir en boucle à temps fixe ou le garder une boucle à temps variable et faire sûr que tous vos comptes mathématiques pour l'interpolation du temps. Ou même un mélange de ceux-ci, en quelque sorte.Vous pouvez certainement avoir une
myIdle
fonction à laquelle vous passezglutIdleFunc
, et dans ce cadre, mesurer le temps écoulé, la diviser par votre pas de temps fixe et appeler une autre fonction autant de fois.Voici ce qu'il ne faut pas faire:
fixedTimestep ne serait pas appelé toutes les 10 millisecondes. En fait, cela fonctionnerait plus lentement qu'en temps réel, et vous pourriez finir par truquer les nombres pour compenser quand vraiment la racine du problème réside dans la perte du reste lorsque vous divisez
timeDifference
par 10.Au lieu de cela, faites quelque chose comme ceci:
Maintenant, cette structure de boucle garantit que votre
fixedTimestep
fonction sera appelée une fois toutes les 10 millisecondes, sans perdre de millisecondes comme reste ou quoi que ce soit du genre.En raison de la nature multitâche des systèmes d'exploitation, vous ne pouvez pas compter sur une fonction appelée exactement toutes les 10 millisecondes; mais la boucle ci-dessus se produirait assez rapidement pour être, espérons-le, suffisamment proche, et vous pouvez supposer que chaque appel de
fixedTimestep
incrémenterait votre simulation d'une valeur de 10 millisecondes.Veuillez également lire cette réponse et en particulier les liens fournis dans la réponse.
la source