Une boucle de jeu doit-elle être basée sur des pas de temps fixes ou variables? Est-ce qu'on est toujours supérieur ou est-ce que le bon choix varie selon les jeux?
Pas de temps variable
Les mises à jour physiques reçoivent un argument "temps écoulé depuis la dernière mise à jour" et sont donc dépendantes du nombre d'images par seconde. Cela peut vouloir dire faire des calculs comme position += distancePerSecond * timeElapsed
.
Avantages : lisse, plus facile à coder
Inconvénients : non déterministe, imprévisible à très petites ou grandes étapes
Exemple de deWiTTERS :
while( game_is_running ) {
prev_frame_tick = curr_frame_tick;
curr_frame_tick = GetTickCount();
update( curr_frame_tick - prev_frame_tick );
render();
}
Pas de temps fixe
Les mises à jour peuvent même ne pas accepter un "temps écoulé", car elles supposent que chaque mise à jour dure une période donnée. Les calculs peuvent être faits comme position += distancePerUpdate
. L'exemple inclut une interpolation pendant le rendu.
Avantages : prévisible, déterministe (plus facile à synchroniser au réseau?), Code de calcul plus clair
Inconvénients : non synchronisé pour surveiller v-sync (provoque des graphiques instables sauf si vous interpolez), cadence maximale limitée (sauf si vous interpolez), difficile à travailler dans des cadres assumer des pas de temps variables (comme Pyglet ou Flixel )
Exemple de deWiTTERS :
while( game_is_running ) {
while( GetTickCount() > next_game_tick ) {
update();
next_game_tick += SKIP_TICKS;
}
interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
/ float( SKIP_TICKS );
render( interpolation );
}
Quelques ressources
- Gaffer sur les jeux: Fixez votre timestep!
- L'article de la boucle de jeu de deWitter
- Les FPS de Quake 3 affectent la physique des sauts - prétendument une des raisons pour lesquelles Doom 3 est bloqué en mode image à 60fps?
- Flixel nécessite un pas de temps variable (je pense que cela est déterminé par Flash) alors que Flashpunk autorise les deux types.
- Le manuel de Box2D § Simuler le monde de Box2D suggère qu'il utilise des pas de temps constants.
la source
Réponses:
Il y a deux problèmes liés à la question.
Dans Glen fielder, fixez votre pas de temps, il dit de "libérer la physique". Cela signifie que votre fréquence d'actualisation physique ne doit pas être liée à votre fréquence d'images.
Erin Catto recommande également cela dans Box2D.
Le taux de progression de la physique doit-il être lié à votre fréquence d'images? Non.
Les pensées d'Erin sur le pas fixe vs le pas variable:
Les pensées de Glen sur le pas fixe vs variable:
La physique devrait-elle être développée avec des deltas constants? Oui.
La façon de faire progresser la physique avec des deltas constants et de ne pas lier votre fréquence d'actualisation physique à la cadence est d'utiliser un accumulateur de temps. Dans mon jeu, je vais encore plus loin. J'applique une fonction de lissage au temps entrant. De cette façon, les pointes FPS importantes n'entraînent pas un saut physique trop important dans la physique, elles sont simulées plus rapidement pour une image ou deux.
Vous mentionnez qu'avec un taux fixe, la physique ne serait pas synchronisée avec l'affichage. Cela est vrai si le taux de physique cible est proche de la fréquence d'images cible. C'est pire le taux de trame est plus grand que le taux de physique. En général, il est préférable de cibler un taux de mise à jour physique égal au double de votre FPS cible, si vous en avez les moyens.
Si vous ne pouvez pas vous permettre une cadence de mise à jour physique importante, envisagez d'interpoler les positions des graphiques entre les images pour que les graphiques dessinés semblent bouger plus doucement que la physique ne le fait réellement.
la source
Je pense qu'il y a vraiment 3 options, mais vous les listez comme seulement 2:
Option 1
Ne fais rien. Essayez de mettre à jour et de rendre à un certain intervalle, par exemple 60 fois par seconde. Si elle prend du retard, laissez-la et ne vous inquiétez pas. Les jeux ralentiront lentement si le processeur ne parvient pas à suivre votre rythme. Cette option ne fonctionnera pas du tout pour les jeux multi-utilisateurs en temps réel, mais convient pour les jeux à un joueur et a été utilisée avec succès dans de nombreux jeux.
Option 2
Utilisez le temps de décalage entre chaque mise à jour pour modifier le mouvement des objets. En théorie, c'est bien, surtout si rien dans votre jeu n'accélère ou ne ralentit, mais se déplace à une vitesse constante. En pratique, de nombreux développeurs l’implémentent mal, ce qui peut conduire à une détection et à une physique des collisions incohérentes. Il semble que certains développeurs pensent que cette méthode est plus simple qu’elle ne l’est. Si vous souhaitez utiliser cette option, vous devez considérablement améliorer votre jeu et faire ressortir des algorithmes et des calculs compliqués, par exemple en utilisant un intégrateur physique Verlet (plutôt que l'Euler standard utilisé par la plupart des gens) et en utilisant des rayons pour détecter les collisions. plutôt que de simples contrôles à distance Pythagore. Il y a quelque temps, j'ai posé une question à ce sujet sur Stack Overflow et j'ai obtenu d'excellentes réponses:
https://stackoverflow.com/questions/153507/calculate-the-position-of-an-accelerating-body-after-a-certain- time
Option 3
Utilisez l'approche "Fixez votre pas de temps" de Gaffer. Mettez à jour le jeu par étapes fixes comme dans l'option 1, mais faites-le plusieurs fois par image rendue (en fonction du temps écoulé), de sorte que la logique du jeu reste en temps réel tout en conservant des étapes discrètes. De cette façon, la logique de jeu, comme les intégrateurs d'Euler, et la détection de collision simple fonctionnent toujours. Vous avez également la possibilité d'interpoler des animations graphiques en fonction du temps delta, mais cela ne concerne que les effets visuels et rien qui affecte votre logique de jeu principale. Vous pouvez éventuellement avoir des problèmes si vos mises à jour sont très intensives. Si les mises à jour prennent du retard, vous en aurez de plus en plus à suivre, ce qui pourrait rendre votre jeu encore moins réactif.
Personnellement, j'aime bien l'option 1 lorsque je peux m'en tirer et l'option 3 lorsque je dois effectuer une synchronisation en temps réel. Je respecte l’option 2 peut être une bonne option lorsque vous savez ce que vous faites, mais je connais suffisamment mes limites pour en rester à l’écart.
la source
J'aime beaucoup la manière dont XNA Framework implémente le pas de temps fixe. Si un appel de tirage prend un peu trop longtemps, il appelle la mise à jour à plusieurs reprises jusqu'à ce qu'il "rattrape". Shawn Hargreaves le décrit ici:
http://blogs.msdn.com/b/shawnhar/archive/2007/11/23/game-timing-in-xna-game-studio-2-0.aspx
À mon avis, le plus gros professionnel à ce sujet est celui que vous avez mentionné, qui simplifie considérablement tous les calculs de code de jeu, car vous n'avez pas à inclure cette variable de temps dans tous les sens.
note: xna supporte aussi le timestep variable, c'est juste un paramètre.
la source
Il existe une autre option: découpler la mise à jour du jeu et la mise à jour de la physique. Adapter le moteur physique au timestep du jeu pose un problème si vous corrigez votre timestep (le problème de la perte de contrôle du fait que l'intégration a besoin de plus de timesteps qui prennent plus de temps et de plus de temps), ou si vous le modifiez et obtenez une physique sans fioritures.
La solution que je vois souvent est de faire fonctionner la physique sur un pas de temps fixe, dans un thread différent (sur un noyau différent). Le jeu interpole ou extrapole en fonction des deux dernières images valides qu'il peut récupérer. L'interpolation ajoute un certain retard, l'extrapolation ajoute une certaine incertitude, mais votre physique sera stable et ne fera pas perdre le contrôle de votre temps.
Ce n'est pas trivial à mettre en œuvre, mais pourrait se révéler une preuve pour l'avenir.
la source
Personnellement, j'utilise une variation de pas de temps variable (qui est en quelque sorte un hybride de fixe et de variable, je pense). J'ai testé ce système de chronométrage de plusieurs manières, et je me sers de l'utiliser pour de nombreux projets. Est-ce que je le recommande pour tout? Probablement pas.
Mes boucles de jeu calculent la quantité d'images à mettre à jour (appelons-le F), puis effectuent F mises à jour de la logique discrète. Chaque mise à jour logique suppose une unité de temps constante (qui correspond souvent à 1 / 100ème de seconde dans mes jeux). Chaque mise à jour est effectuée en séquence jusqu'à ce que toutes les mises à jour de la logique discrète F soient effectuées.
Pourquoi des mises à jour discrètes en étapes logiques? Eh bien, si vous essayez d’utiliser des étapes continues et que vous rencontrez un problème physique, les vitesses et les distances à parcourir calculées sont multipliées par une valeur énorme de F.
Une mauvaise mise en œuvre de ceci ne ferait que F = heure actuelle - dernières mises à jour de l'heure du cadre. Mais si les calculs prennent trop de retard (parfois en raison de circonstances indépendantes de votre volonté comme un autre processus volant du temps CPU), vous verrez rapidement de terribles sauts. Rapidement, le FPS stable que vous avez essayé de maintenir devient SPF.
Dans mon jeu, j'autorise un ralentissement «en douceur» (en quelque sorte) pour limiter la quantité de rattrapage logique qui devrait être possible entre deux matchs nuls. Je le fais en fixant: F = min (F, MAX_FRAME_DELTA) qui a généralement MAX_FRAME_DELTA = 2/100 * s ou 3/100 * s. Ainsi, au lieu de sauter les images trop loin derrière la logique du jeu, supprimez toute perte d’image importante (ce qui ralentit le processus), récupérez quelques images, dessinez, puis réessayez.
En faisant cela, je m'assure également que les commandes du lecteur restent synchronisées avec ce qui est réellement affiché à l'écran.
Le pseudocode du produit final ressemble à ceci (le delta est F mentionné plus haut):
Ce type de mise à jour ne convient pas à tout, mais pour les jeux d'arcade, je préférerais que le jeu ralentisse, car il y a beaucoup de choses qui se passent plutôt que de manquer d'images et de perdre le contrôle du joueur. Je préfère également cela à d’autres approches à pas de temps variable qui finissent par avoir des problèmes irréversibles causés par une perte de trame.
la source
Cette solution ne s'applique pas à tout, mais il existe un autre niveau de variable timestep - variable timestep pour chaque objet du monde.
Cela semble compliqué, et cela peut être, mais considérez-le comme une modélisation de votre jeu comme une simulation d'événement discret. Chaque mouvement de joueur peut être représenté comme un événement qui commence au début du mouvement et se termine à la fin du mouvement. S'il existe une interaction nécessitant le fractionnement de l'événement (une collision par exemple), l'événement est annulé et un autre événement est ajouté à la file d'attente des événements (qui est probablement une file d'attente prioritaire triée par heure de fin de l'événement).
Le rendu est totalement détaché de la file d'attente des événements. Le moteur d’affichage interpole les points entre les heures de début et de fin de l’événement selon les besoins, et peut être aussi précis ou aussi bâclé que nécessaire dans cette estimation.
Pour voir une implémentation complexe de ce modèle, voir le simulateur d’espace EXOFLIGHT . Il utilise un modèle d'exécution différent de la plupart des simulateurs de vol - un modèle basé sur les événements, plutôt que le modèle traditionnel à tranche de temps fixe. La boucle principale de base de ce type de simulation ressemble à ceci, en pseudo-code:
La principale raison d’en utiliser un dans un simulateur spatial est la nécessité de fournir une accélération temporelle arbitraire sans perte de précision. Certaines missions dans EXOFLIGHT peuvent prendre des années de jeu et même une option d’accélération 32x serait insuffisante. Vous auriez besoin de plus de 1 000 000 x d’accélération pour un sim utilisable, ce qui est difficile à réaliser dans un modèle en tranche de temps. Avec le modèle basé sur les événements, nous obtenons des taux de temps arbitraires, allant de 1 s = 7 ms à 1 s = 1 an.
Changer le taux de temps ne change pas le comportement de la sim, qui est une caractéristique essentielle. S'il n'y a pas assez de puissance CPU disponible pour exécuter le simulateur à la vitesse souhaitée, les événements s'empileront et nous pourrons limiter l'actualisation de l'interface utilisateur jusqu'à ce que la file d'attente des événements soit effacée. De même, nous pouvons faire avancer la simulation autant que nous le souhaitons et nous assurer que nous ne gaspillons pas le processeur ni ne sacrifions la précision.
En résumé, nous pouvons modéliser un véhicule sur une longue orbite tranquille (en utilisant l’intégration Runge-Kutta) et un autre véhicule rebondissant simultanément sur le sol - les deux véhicules seront simulés avec une précision appropriée, car nous n’avons pas d’horodatage global.
Inconvénients: Complexité et absence de tout moteur physique standard prenant en charge ce modèle :)
la source
Le pas de temps fixe est utile pour prendre en compte la précision en virgule flottante et rendre les mises à jour cohérentes.
C'est un simple morceau de code, il serait donc utile de l'essayer et de voir si cela fonctionne pour votre jeu.
Le principal problème de l'utilisation d'un pas de temps fixe est que les joueurs dotés d'un ordinateur rapide ne pourront pas utiliser cette vitesse. Rendu à 100 images par seconde lorsque le jeu est mis à jour uniquement à 30 images par seconde est identique à un rendu à 30 images par seconde.
Cela dit, il est possible d’utiliser plus d’un pas de temps fixe. 60fps peuvent être utilisés pour mettre à jour des objets triviaux (tels que des interfaces utilisateur ou des images-objets animées) et 30fps pour mettre à jour des systèmes non triviaux (tels que la physique et) et même des minuteries plus lentes à effectuer en arrière-plan, telles que la suppression d'objets inutilisés, de ressources, etc.
la source
En plus de ce que vous avez déjà dit, cela peut venir du sentiment que vous voulez que votre jeu ait. À moins que vous ne puissiez garantir que vous aurez toujours une fréquence d'images constante, vous risquez probablement un ralentissement quelque part et des pas de temps fixes et variables seront très différents. Fixé aura l'effet de ralentir votre jeu pendant un certain temps, ce qui peut parfois être l'effet voulu (regardez un jeu de tir à l'ancienne, comme Ikaruga, où des explosions massives provoquent un ralentissement après avoir battu un boss). Des pas de temps variables maintiendront les choses à la vitesse voulue, mais il se peut que des changements soudains de position, etc., empêchent le joueur d'effectuer ses actions avec précision.
Je ne vois pas vraiment qu'un pas de temps fixe faciliterait les choses sur un réseau, ils seraient tous légèrement désynchronisés pour commencer et un ralentissement sur une machine, mais pas une autre ne ferait en sorte que les choses soient plus désynchronisées.
Je me suis toujours penché personnellement pour l'approche variable, mais ces articles ont des choses intéressantes à penser. Cependant, j’ai encore trouvé des étapes fixes assez courantes, en particulier sur les consoles où l’on pense que le framerate est de 60 ips par rapport aux taux très élevés pouvant être atteints sur un PC.
la source
Utilisez l'approche "Fixez votre pas de temps" de Gaffer. Mettez à jour le jeu par étapes fixes comme dans l'option 1, mais faites-le plusieurs fois par image rendue (en fonction du temps écoulé), de sorte que la logique du jeu reste en temps réel tout en conservant des étapes discrètes. De cette façon, la logique de jeu, comme les intégrateurs d'Euler, et la détection de collision simple fonctionnent toujours. Vous avez également la possibilité d'interpoler des animations graphiques en fonction du temps delta, mais cela ne concerne que les effets visuels et rien qui affecte votre logique de jeu principale. Vous pouvez éventuellement avoir des problèmes si vos mises à jour sont très intensives. Si les mises à jour prennent du retard, vous en aurez de plus en plus à suivre, ce qui pourrait rendre votre jeu encore moins réactif.
Personnellement, j'aime bien l'option 1 lorsque je peux m'en tirer et l'option 3 lorsque je dois effectuer une synchronisation en temps réel. Je respecte l’option 2 peut être une bonne option lorsque vous savez ce que vous faites, mais je connais suffisamment mes limites pour ne pas en subir les conséquences
la source
J'ai trouvé que des pas de temps fixes synchronisés à 60fps donnaient une animation en miroir. Ceci est particulièrement important pour les applications de réalité virtuelle. Toute autre chose est physiquement nauséabonde.
Des pas de temps variables ne conviennent pas à la réalité virtuelle. Jetez un coup d’œil à quelques exemples d’Unity VR qui utilisent des pas de temps variables. C'est désagréable.
La règle est que si votre jeu 3D est fluide en mode VR, il sera excellent en mode non-VR.
Comparez ces deux (applications de carton VR)
(Pas de temps variables)
(Timesteps fixes)
Votre jeu doit être multithread afin d'obtenir un timestep / framerate cohérent. La physique, l'interface utilisateur et le rendu doivent être séparés en threads dédiés. Il est hideux de les synchroniser avec PITA, mais les résultats obtenus vous permettent d'obtenir le rendu lisse que vous souhaitez (en particulier pour VR).
Les jeux mobiles sont en particulier. difficile car les processeurs et les GPU intégrés ont des performances limitées. Utilisez GLSL (argot) avec parcimonie pour décharger le plus de travail possible de la CPU. Attention, le fait de transmettre des paramètres au GPU consomme des ressources de bus.
Gardez toujours votre framerate affiché pendant le développement. Le vrai jeu est de le garder fixe à 60fps. C'est le taux de synchronisation natif pour la plupart des écrans et également pour la plupart des globes oculaires.
La structure que vous utilisez doit pouvoir vous informer d'une demande de synchronisation ou utiliser un minuteur. N'insérez pas de délai sommeil / attente pour y parvenir - même de légères variations sont perceptibles.
la source
Les intervalles de temps variables concernent les procédures qui doivent être exécutées aussi souvent que possible: cycles de rendu, gestion des événements, fonctions réseau, etc.
Les pas de temps fixes sont indiqués lorsque vous avez besoin que quelque chose soit prévisible et stable. Cela inclut, mais ne se limite pas à la physique et à la détection de collision.
Concrètement, la physique et la détection de collision devraient être dissociées de tout le reste, sur son propre pas de temps. La raison pour laquelle de telles procédures sont exécutées sur un petit pas de temps fixe est de les garder précises. La magnitude des impulsions dépend fortement du temps. Si l'intervalle devient trop grand, la simulation devient instable et des trucs fous se produisent comme une balle qui rebondit sur le sol ou qui sortent du monde du jeu, ce qui n'est pas souhaitable.
Tout le reste peut fonctionner à un pas de temps variable (bien que, professionnellement, c'est souvent une bonne idée de permettre le verrouillage du rendu sur un pas de temps fixe). Pour qu'un moteur de jeu soit réactif, des éléments tels que les messages réseau et les entrées utilisateur doivent être traités dès que possible, ce qui signifie que l'intervalle entre les scrutations doit idéalement être aussi court que possible. Cela signifie généralement variable.
Tout le reste peut être traité de manière asynchrone, faisant de la synchronisation un point discutable.
la source