Conceptuellement, comment fonctionne la relecture dans un jeu?

145

J'étais assez curieux de savoir comment la relecture pourrait être implémentée dans un jeu.

Au départ, je pensais qu'il y aurait juste une liste de commandes de chaque action de joueur / ai qui a été prise dans le jeu, puis il «rejoue» le jeu et laisse le moteur rendre comme d'habitude. Cependant, je l' ai regardé rejoue dans les jeux FPS / RTS, et sur une inspection minutieuse même des choses comme les particules et problèmes graphiques / sonores sont compatibles (et les glitches sont généralement en constante).

Alors, comment cela se passe-t-il. Dans les jeux à angle de caméra fixe, je pensais qu'il pourrait simplement écrire chaque image de la scène entière dans un flux qui est stocké, puis rejoue simplement le flux, mais cela ne semble pas suffisant pour les jeux qui vous permettent de faire une pause et de déplacer la caméra. autour. Vous auriez à stocker les emplacements de tout dans la scène à tout moment (non?). Donc, pour des choses comme les particules, c'est beaucoup de données à pousser, ce qui semble être un tirage important sur les performances du jeu tout en jouant.

Steven Evers
la source
10
Les replays originaux de Star Craft n'étaient en fait PAS cohérents. Vous pouvez regarder le même match deux fois et voir des résultats assez différents.
Andres
1
@Andres: Intéressant, je n'avais pas remarqué. En particulier, pour le genre RTS, je pensais à Company Of Heroes.
Steven Evers
4
Pour clarifier ce que je pense que SnOrfus demande: certains jeux (Uncharted 2, Halo 3, même Battlefield 2) vous permettent d'enregistrer un jeu dans son intégralité. Une fois le jeu terminé, vous pouvez le rejouer à une vitesse désignée et voler à travers le niveau pendant que l'action se déroule, en le visualisant depuis n'importe quelle position sur la carte. Je suppose donc qu'il s'agit d'enregistrer les mouvements de tous les joueurs / objets et non de quelque chose à voir avec la mémoire tampon vidéo.
Sean
1
@Sean O'Hollaren: Oui, c'est exact.
Steven Evers
1
Ensuite, j'ajouterai également pour les jeux de course automobile où les replays sont presque par défaut. Je suis à peu près sûr que l'emplacement des modèles est enregistré, puis tout passe par le moteur.
d -_- b

Réponses:

61

Je pense que votre première pensée était correcte. Pour créer une relecture, vous stockez toutes les entrées reçues de l'utilisateur (avec le numéro de l'image à laquelle elles ont été reçues) ainsi que les valeurs initiales de tout générateur de nombres aléatoires. Pour rejouer le jeu, vous réinitialisez vos PRNG en utilisant les graines sauvegardées et alimentez le moteur de jeu la même séquence d'entrée (synchronisée avec les numéros d'image). Étant donné que de nombreux jeux mettront à jour l'état du jeu en fonction du temps qui s'écoule entre les images, vous devrez peut-être également stocker la longueur de chaque image.

Peter Ruderman
la source
Les numéros d'images peuvent ne pas être une bonne référence car la relecture peut s'exécuter à une fréquence d'images différente de celle du jeu en direct.
Ben S
5
@Ben: Framerate ne fait aucune différence, puisque les numéros d'images seront toujours les mêmes. C'est la bonne réponse.
BlueRaja - Danny Pflughoeft
14
Les cadres graphiques et les «cadres» (ou itérations) du moteur ne sont pas nécessairement les mêmes. Dans de nombreux jeux plus anciens, le moteur s'est mis à jour au même rythme que les graphiques, dans une boucle principale. Avec les moteurs modernes, les graphismes sont souvent autorisés à se mettre à jour aussi vite que le GPU le permet, le moteur tournant au niveau requis pour une bonne résolution cohérente de la dynamique du jeu (souvent un moteur physique).
Dan Bryant
3
@iamgopal: si vous connaissez l'état du générateur de nombres pseudo-aléatoires, alors ce problème est déjà résolu. Une autre méthode peut consister à traiter les nombres aléatoires comme une autre forme d'entrée et à les sauvegarder à côté des touches et autres.
Kylotan
1
J'aimerais ajouter que cette approche nécessite que votre moteur de jeu soit déterministe et qu'il fonctionne avec un pas de temps fixe. Je pense que tous les jeux RTS de Blizzard ont été construits de cette façon. Les jeux non déterministes incluraient des données de synchronisation supplémentaires pour assurer la cohérence à long terme.
John Leidegren
28

Starcraft et Starcraft: Brood War avaient une fonction de relecture. Une fois le match terminé, vous pouvez choisir d'enregistrer la rediffusion pour l'afficher plus tard. Lors de la relecture, vous pouvez faire défiler la carte et cliquer sur les unités et les bâtiments, mais ne pas modifier leurs comportements.

Je me souviens avoir regardé une fois une rediffusion d'un match qui avait été joué dans le jeu original, mais la rediffusion était visionnée dans Brood War. Pour ceux qui ne sont pas familiers, Brood War contient toutes les unités et bâtiments d'origine, ainsi qu'une variété de nouveaux. Dans le jeu original, le joueur avait vaincu l'ordinateur en créant des unités que l'ordinateur ne pouvait pas facilement contrer. Quand j'ai joué la rediffusion dans Brood War, l'ordinateur avait accès à différentes unités, qu'il a créées et utilisées pour vaincre le joueur. Ainsi, le même fichier de relecture a abouti à un gagnant différent selon la version de Starcraft qui jouait le fichier.

J'ai toujours trouvé le concept fascinant. Il semblerait que la fonction de relecture fonctionnait en enregistrant toutes les entrées du lecteur et supposait que l'ordinateur répondrait à ces stimuli exactement de la même manière à chaque fois. Lorsque les entrées du joueur ont été introduites dans le replayer Starcraft original, le jeu s'est déroulé exactement comme il l'a fait dans le match original. Lorsque la même entrée exacte a été introduite dans le replayer Brood War, l'ordinateur a réagi différemment, a créé des unités plus fortes et a remporté la partie.

Quelque chose à garder à l'esprit si vous écrivez un moteur de relecture.

Bobwise
la source
6
+1: Très intéressant. Je n'avais jamais entendu parler de ça. Fournit un bon aperçu de la façon dont ils l'ont développé.
Steven Evers
18

Il existe deux méthodes principales:

  1. Stocker des événements (tels que des actions de joueur / IA) - comme vous le dites.
  2. État de stockage (état complet du jeu, par exemple les emplacements des objets, à des moments consécutifs).

Cela dépend de ce que vous voulez faire. Parfois, le stockage des événements est préférable, car cela prend généralement beaucoup moins de mémoire. D'un autre côté, si vous souhaitez fournir des rediffusions qui peuvent être jouées à différentes vitesses et à partir de différents points de départ, il est préférable de stocker les états. Lors du stockage des états, vous pouvez également décider de les stocker après chaque événement ou de les stocker seulement 12 ou 25 fois par seconde - cela peut réduire la taille de votre relecture et faciliter leur retour / avance rapide.

Notez que "état" ne signifie pas état graphique. Plus quelque chose comme les positions des unités, l'état des ressources et ainsi de suite. Des choses comme les graphiques, les systèmes de particules, etc. sont généralement déterministes et peuvent être stockées sous la forme "animation X, temps Y: Z".

Parfois, les replays sont utilisés comme système anti-tricheur. Ensuite, stocker les événements est probablement le meilleur ici.

liori
la source
10

Techniquement, vous devez écrire votre moteur pour qu'il soit déterministe, ce n'est pas un hasard. En supposant qu'un personnage dans le jeu vise le bras d'un adversaire et tire une arme, alors le même montant de dégâts doit être appliqué à l'adversaire dans tous les cas.

En supposant qu'une bombe explose à l'emplacement X, les particules produites par cette explosion devraient toujours donner le même résultat visuel. Si vous avez besoin du caractère aléatoire, créez un ensemble de nombres aléatoires, sélectionnez une valeur de départ lorsque le jeu est joué et enregistrez cette valeur de départ dans la relecture.

En général, avoir du hasard dans un jeu est une mauvaise idée. Même pour des choses comme le multijoueur, vous ne pouvez pas avoir la moitié de vos joueurs capables de voir autour d'une explosion alors que les autres ne le peuvent pas simplement parce qu'ils n'ont pas obtenu la bonne valeur aléatoire.

Rendez tout déterministe, et tout devrait aller bien.

Timothy Baldridge
la source
1
Et l'IA? L'IA n'est-elle pas aléatoire?
Jesse Jashinsky
18
Ce n'est vraiment pas nécessaire. Utilisez des nombres pseudo-aléatoires prédéfinis pour tous les événements aléatoires et enregistrez la valeur de départ dans le fichier de relecture. De cette façon, les mêmes nombres «aléatoires» seront générés lors de la relecture.
Ben S
13
-1 pour un malentendu clair sur le fonctionnement de "l'aléatoire" dans les ordinateurs
BlueRaja - Danny Pflughoeft
10
euh ... non ... Je suis parfaitement conscient qu'il n'y a pas de "vrai" hasard. Cependant, la plupart des gens essaient de contourner ce problème en définissant leur graine aléatoire sur quelque chose comme l'heure système. Cependant, ce que je dis, c'est qu'une telle chose ne devrait pas être faite. Je m'en fiche s'il utilise l'API système ou une table prédéfinie de nombres aléatoires. Ce que j'ai dit au départ était correct. Chaque fonction de son moteur doit produire le même résultat en fonction de ses entrées. Le temps ne devrait jamais être un facteur.
Timothy Baldridge
2
Si les particules n'ont aucune interaction significative avec la mécanique du jeu, peu importe si les RNG sont différents pour elles. Cela aiderait dans le cas d'une simulation synchronisée en réseau (comme c'est le cas dans la plupart des jeux RTS, et dans de nombreux autres genres de jeux), car c'est un peu moins que la simulation doive synchroniser chaque image (les effets de particules obtiennent juste mis à jour individuellement).
RCIX
10

Étant donné l' état initial et une série d'actions avec des horodatages , il suffit de parcourir la séquence car les actions enregistrées sont censées se produire et être relues.

Pour que les événements aléatoires se reproduisent exactement de la même manière, utilisez des nombres pseudo-aléatoires prédéfinis et enregistrez la valeur de départ dans le fichier de relecture.

Tant que vous utilisez le même algorithme pour générer les nombres aléatoires à partir de la graine, vous pouvez recréer tous les événements tels qu'ils se sont produits dans le jeu en direct sans avoir besoin d'instantanés complets de l'état du jeu.

Cela nécessitera que les rediffusions soient regardées séquentiellement , mais c'est assez normal pour les rediffusions de jeu (voir Starcraft 2). Si vous souhaitez autoriser l'accès aléatoire à la chronologie, vous pouvez prendre des instantanés de l'état complet à des intervalles définis (par exemple, toutes les minutes), pour sauter dans la chronologie avec une granularité définie.

Ben S
la source
Si vous re-ensemencez toutes les secondes (disons 5 ou 10), il serait assez facile d'enregistrer dans votre flux de relecture et de permettre également de sauter en avant ou en arrière (pour PRNG "images clés" essentiellement).
Wedge
7

NVidia PhysX (un moteur de simulation physique souvent utilisé dans les jeux) est capable d'enregistrer l'état complet de la scène physique au fil du temps. Cela intègre toutes les entrées de pilotage du moteur de jeu, ce qui signifie que vous n'avez pas besoin de suivre les graines de nombres aléatoires comme d'autres l'ont suggéré. Si vous effectuez ce vidage de scène, vous pouvez le rejouer dans un outil externe (fourni par NVidia), ce qui est très pratique pour dépister les problèmes avec vos modèles physiques. Cependant, vous pouvez également utiliser le même flux physique pour piloter votre moteur graphique, ce qui vous permettrait alors d'avoir un contrôle normal de la caméra, car seule la physique pilotant les graphiques a été enregistrée. Dans de nombreux jeux, cela inclut les effets de particules (PhysX comprend des systèmes de particules très sophistiqués.) En ce qui concerne le son, je suppose qu'il est enregistré textuellement (sous forme de flux sonore), mais je '

Dan Bryant
la source
4

Votre idée originale est juste, et pour les effets vraiment complexes, ils ne se souviennent pas exclusivement. Par exemple, le système de relecture de Warcraft 3 ne stocke pas l'état des animations, ou des effets de particules dans le cas d'effets aléatoires, etc. En outre, la plupart des choses peuvent être calculées par ordinateur à partir d'un point de départ de manière déterministe, donc pour la plupart des systèmes qui utilisent des variables aléatoires (une explosion de particules qui donne un décalage aléatoire, par exemple), tout ce dont vous auriez besoin est l'heure de l'effet et la graine aléatoire. Vous pourriez alors régénérer l'effet sans vraiment savoir à quoi il finira par ressembler ... sachant qu'il passe par un chemin de code déterministe.

En y pensant purement conceptuellement, pour rejouer une chronologie des événements, tout ce dont vous avez besoin sont les actions de l'utilisateur. Le programme réagira exactement de la même manière, sauf dans le cas de variables aléatoires. Dans ce scénario, vous pouvez soit ignorer le caractère aléatoire (est-ce vraiment important que les effets se ressemblent EXACTEMENT, ou peuvent-ils être régénérés aléatoirement), ou stocker la valeur de départ et simuler le caractère aléatoire.

Stefan Valianu
la source
3

Jette mes deux sous.

Dépend de ce que vous voulez, la relecture peut être effectuée via

  1. Enregistrement de la mémoire tampon vidéo et relecture plus tard,
  2. Capture de l'état de l'objet à chaque image et relecture plus tard,

La plupart du temps, les gens veulent une rediffusion interactive, 2. est donc la voie à suivre. Ensuite, en fonction de vos contraintes, il existe plusieurs façons d'optimiser ce processus

  • s'assurer que le système est une simulation déterministe *, de sorte que chaque entrée génère une sortie cohérente et attendue
  • si le caractère aléatoire est requis, assurez-vous que les nombres aléatoires peuvent être reproduits exactement à un moment ultérieur [regardez l'ensemencement avec les générateurs de nombres pseudo aléatoires PRNG, ou utilisez des ensembles aléatoires prédéfinis]
  • divisez les éléments du jeu en éléments «mécaniques» et «esthétiques». les éléments mécaniques affectent le résultat [par exemple la chute de la colonne et le chemin bloquant], les éléments esthétiques sont pour le spectacle et n'influencent aucun processus de prise de décision dans le système [par exemple les effets visuels de particules comme les étincelles].

C'est vraiment un sujet fascinant. Je me souviens qu'un titre de lancement pour Xbox Wreckless original avait une bonne fonctionnalité de lecture. Malheureusement, à plusieurs reprises, la rediffusion aurait échoué;)

oh ouais, comment pourrait-on oublier Blinx Time Sweeper ! super rejeu interactif qui a été intégré dans la mécanique de jeu réelle!


* = il semble qu'il y ait des commentaires concernant le pas de temps. J'utilise la «simulation» ici pour capturer cette fonctionnalité. à la base, votre moteur doit être capable de produire des intervalles de temps discrets. même si une image de relecture prend plus ou moins de temps à traiter que l'original, le système doit percevoir que le même delta de temps s'est écoulé. cela signifie enregistrer le pas de temps de trame avec chaque entrée enregistrée, et fournir ce delta à votre horloge moteur.

johnny g
la source
2

Peut-être pourriez-vous simplement enregistrer une pile de commandes envoyées par chaque joueur. Ainsi, au lieu de sauver qu'une bombe explose à un certain moment et à un certain moment, ou qu'une certaine voiture est détruite, vous sauvegardez simplement les touches envoyées par chaque joueur. Ensuite, dans la rediffusion, vous simulez simplement le jeu comme il se serait produit avec ces presses. J'ai l'impression que cela a le potentiel de prendre moins de place, mais je n'ai jamais travaillé sur un système de relecture comme celui-là.

Question intéressante, cependant. Je serais intéressé par la façon dont cela se fait dans les jeux professionnels.

TroisièmeD3gree
la source
2

Dan Bryant

En outre, l'enregistrement de graines aléatoires ne serait pas suffisant pour le support de rembobinage, car la progression aléatoire n'est pas une procédure réversible sans un support spécial dans toute la logique reposant sur le caractère aléatoire. Il est plus flexible d'enregistrer les résultats des opérations aléatoires dans le cadre du flux d'événements.

C'est exactement ce que j'ai pensé au début lorsque j'essayais de comprendre comment ils avaient réussi à faire en sorte que le jeu soit toujours le même à chaque fois. Avec Doom, j'ai pensé au hasard des tournages: D. Stockez n'importe quel nombre aléatoire utilisé, j'ai découvert que cela pourrait être une solution. C'était avant que je tombe sur un papier pdf sur la technologie Crysis. Certaines textures y résonnent et la disposition de l'herbe ou des arbres semblaient utiliser la pseudo-aléatoire avec des semences réversibles fixes pour que vous ne voyiez pas la disposition modifiée du bruit, des arbres et de l'herbe à chaque fois que vous regardez!

Éviter dans le même temps, de stocker des millions d'arbres et de positionner des arbres d'herbe. Apparemment, une séquence pseudo aléatoire peut rejouer la même chose à tout moment, car la logique est fixe, pour simplement créer une fausse séquence de nombres statistiquement aléatoire.

Antonyme
la source
Si vous souhaitez attirer l'attention de Dan sur ce point, ajoutez un commentaire sous sa contribution - sinon il ne la verra probablement pas.
halfer
Cela pourrait-il être dû au fait que je ne suis qu'un invité, mais que je n'ai pas vu de fonction "ajouter un commentaire" dans le message parent, a répondu Dan, encore moins la réponse de Dan. J'ai vu qu'il y avait une fonction d'édition-amélioration, même pour les messages qui ne sont pas les miens, mais comment cela fonctionne-t-il?
Antonymous
Ah, bonne question! Il semble que vous ayez besoin de 50 points de répétition pour commenter des questions ou des réponses autres que les vôtres - mes excuses. 50 est cependant très facile à obtenir - il suffit généralement de quelques contributions utiles. Oui, vous pouvez modifier les questions et réponses d'autres personnes, mais vos modifications seront examinées par d'autres jusqu'à ce que vous arriviez à 2000. Consultez le tableau de vos privilèges ici .
halfer
1

Le problème d'avoir une rediffusion cohérente est le même (enfin, plus facile) que d'avoir un jeu multijoueur cohérent.

Comme d'autres l'ont mentionné précédemment, les rediffusions dans les jeux RTS sont stockées en enregistrant toutes les entrées (ce qui a un effet. Le défilement n'a aucun effet.) Le multijoueur transmet également toutes les entrées.

L'enregistrement de toutes les entrées n'est pas seulement une supposition - il existe une bibliothèque pour lire les replays de Warcraft3 avec le révèle.

l'entrée inclut les horodatages pour cette réponse.

Franky
la source
Non, ce n'est pas la même chose (ou plus facile) qu'un jeu MP cohérent. Lorsque vous jouez à MP, les jeux exigent généralement que tout le monde ait la même version du jeu, ce qui n'est pas nécessairement le cas pour les sessions stockées (car elles peuvent avoir été stockées avec une ancienne version du jeu). Ceci est particulièrement critique si l'un des joueurs est un adversaire de l'IA. Imaginez simplement que vous rejouez un jeu dans lequel une unité a juste un point d'attaque de plus dans une version plus récente que dans la version où elle a été enregistrée. Cela pourrait conduire à un résultat complètement différent.
drakon
-1

Je croirais qu'à certains moments, le jeu prendrait un instantané de l'état de tout (TOUT). Ensuite, lorsque la relecture se produit, une simple utilisation de l'interpolation linéaire peut être utilisée pour combler les «trous». Du moins, c'est ainsi que je pense que cela se ferait.

Vous avez raison de dire que l'enregistrement des entrées ne serait pas fiable / ne garantirait pas la même sortie. Le jeu doit absolument garder une trace de l'état de tous les objets (ou du moins les plus importants)

Bob Fincheimer
la source
2
Non, alimenter les mêmes entrées donnera exactement le même résultat que la première fois. Vous devez simplement vous assurer que vous obtenez le timing correct, en alimentant l'entrée entre les mêmes trames où elle a été reçue à l'origine. La sauvegarde périodique de l'état complet du jeu pourrait nécessiter une quantité colossale de mémoire et produire également des résultats incohérents.
Peter Ruderman
@ Peter, "nourrir les mêmes entrées donnera exactement le même résultat": non. Il y a un élément aléatoire dans de nombreux jeux, qui peut être différent à chaque fois que la relecture est jouée. Vous devez garder une trace de plus que les entrées.
houbysoft
C'est vrai. Vous devez également stocker les graines de vos PRNG (voir ma réponse à cette question).
Peter Ruderman
1
Je sais que cela consomme de la performance et de la mémoire, mais si vous manquez une petite chose en ce qui concerne l'entrée ou les générateurs aléatoires ... ou vraiment quoi que ce soit, la relecture se déroulera sur une horrible tangente!
Bob Fincheimer
@BlueRaja, l'idée d'instantané de mémoire de Bob n'est pas nécessairement si farfelue, même si un bon moteur peut enregistrer des «deltas» d'état plutôt que d'encoder toute la mémoire pour chaque itération. C'est probablement plus facile à prendre en charge au niveau du moteur. En outre, l'enregistrement de graines aléatoires ne serait pas suffisant pour le support de rembobinage, car la progression aléatoire n'est pas une procédure réversible sans un support spécial dans toute la logique reposant sur le caractère aléatoire. Il est plus flexible d'enregistrer les résultats des opérations aléatoires dans le cadre du flux d'événements.
Dan Bryant