Prévision de mouvement pour les non-tireurs

35

Je travaille sur un jeu 2D isométrique multijoueur à échelle modérée, environ 20 à 30 joueurs connectés à la fois à un serveur persistant. J'ai eu quelques difficultés à mettre en place une bonne prédiction de mouvement.

Physique / Mouvement

Le jeu n'a pas de véritable implémentation physique, mais utilise les principes de base pour implémenter le mouvement. Plutôt que de scruter continuellement les entrées, les changements d'état (événements / souris bas / haut / déplacer) servent à modifier l'état de l'entité de personnage contrôlée par le joueur. La direction du joueur (c'est-à-dire / nord-est) est combinée à une vitesse constante et transformée en un véritable vecteur 3D - la vitesse de l'entité.

Dans la boucle principale du jeu, "Update" est appelé avant "Draw". La logique de mise à jour déclenche une "tâche de mise à jour physique" qui suit toutes les entités avec une vitesse non nulle et utilise une intégration très basique pour modifier la position des entités. Par exemple: entity.Position + = entity.Velocity.Scale (ElapsedTime.Seconds) (où "Seconds" est une valeur à virgule flottante, mais la même approche fonctionnerait pour des valeurs entières de la milliseconde).

Le point clé est qu'aucune interpolation n'est utilisée pour le mouvement - le moteur de physique rudimentaire n'a pas de concept d'un "état antérieur" ou d'un "état actuel", mais seulement d'une position et d'une vitesse.

Changement d'état et mise à jour des paquets

Lorsque la vitesse de l'entité de personnage contrôlée par le joueur change, un paquet "move avatar" contenant le type d'action de l'entité (stand, marche, course), la direction (nord-est) et la position actuelle est envoyé au serveur. Cela diffère de la façon dont les jeux 3D à la première personne fonctionnent. Dans un jeu 3D, la vélocité (direction) peut changer image par image lorsque le joueur se déplace. L'envoi de chaque changement d'état transmettrait effectivement un paquet par trame, ce qui serait trop coûteux. Au lieu de cela, les jeux 3D semblent ignorer les changements d'état et envoyer des paquets de "mise à jour d'état" selon un intervalle fixe, par exemple toutes les 80 à 150 ms.

Étant donné que les mises à jour de vitesse et de direction sont beaucoup moins fréquentes dans mon jeu, je peux me permettre d'envoyer chaque changement d'état. Bien que toutes les simulations physiques se déroulent à la même vitesse et soient déterministes, la latence reste un problème. Pour cette raison, j’envoie des paquets de mises à jour de position de routine (similaires à un jeu en 3D) mais beaucoup moins fréquemment - actuellement toutes les 250 ms, mais je pense qu’avec une bonne prédiction, je peux facilement l’accroître vers 500 ms. Le plus gros problème est que je me suis maintenant écarté de la norme: tous les autres documents, guides et exemples en ligne envoient des mises à jour régulières et interpolent entre les deux états. Cela semble incompatible avec mon architecture, et je dois mettre au point un meilleur algorithme de prédiction de mouvement, plus proche d'une architecture (très basique) de "physique en réseau".

Le serveur reçoit ensuite le paquet et détermine la vitesse du joueur à partir de son type de mouvement en fonction d'un script (le joueur est-il capable de courir? Obtenez la vitesse d'exécution du joueur). Une fois qu'il a la vitesse, il le combine avec la direction pour obtenir un vecteur - la vitesse de l'entité. Une détection de triche et une validation de base ont lieu et l'entité côté serveur est mise à jour avec la vitesse, la direction et la position actuelles. Une limitation de base est également effectuée pour empêcher les joueurs d’inonder le serveur de demandes de mouvement.

Après la mise à jour de sa propre entité, le serveur diffuse un paquet "Mise à jour de la position de l'avatar" à tous les autres joueurs à sa portée. Le paquet de mise à jour de position est utilisé pour mettre à jour les simulations physiques côté client (état mondial) des clients distants et effectuer la prévision et la compensation de décalage.

Prédiction et compensation du décalage

Comme mentionné ci-dessus, les clients font autorité pour leur propre position. Sauf en cas de triche ou d'anomalie, l'avatar du client ne sera jamais repositionné par le serveur. Aucune extrapolation ("déplacer maintenant et corriger plus tard") n'est requise pour l'avatar du client - ce que le joueur voit est correct. Cependant, une sorte d'extrapolation ou d'interpolation est requise pour toutes les entités distantes en mouvement. Une sorte de prédiction et / ou de compensation de décalage est clairement requise dans le moteur de simulation / physique local du client.

Problèmes

J'ai eu du mal avec divers algorithmes et j'ai un certain nombre de questions et de problèmes:

  1. Devrais-je extrapoler, interpoler ou les deux? Mon instinct est que je devrais utiliser une extrapolation pure basée sur la vitesse. Le changement d'état est reçu par le client, ce dernier calcule une vitesse "prévue" qui compense le retard, et le système de physique habituel fait le reste. Cependant, tous les autres exemples de code et d'articles sont contraires à la règle: ils semblent tous stocker un certain nombre d'états et effectuer une interpolation sans moteur physique.

  2. Lorsqu'un paquet arrive, j'ai essayé d'interpoler la position du paquet avec la vitesse du paquet sur une période de temps déterminée (par exemple, 200 ms). Je prends ensuite la différence entre la position interpolée et la position "d'erreur" actuelle pour calculer un nouveau vecteur et le place sur l'entité au lieu de la vitesse qui a été envoyée. Cependant, l'hypothèse est qu'un autre paquet arrivera dans cet intervalle de temps, et il est extrêmement difficile de "deviner" quand le prochain paquet arrivera - d'autant plus qu'ils n'arrivent pas tous à des intervalles fixes (c.-à-d. Que les changements d'état). Le concept est-il fondamentalement défectueux ou est-il correct mais nécessite quelques corrections / ajustements?

  3. Que se passe-t-il lorsqu'un lecteur distant s'arrête? Je peux immédiatement arrêter l'entité, mais elle sera placée au "mauvais" endroit jusqu'à ce qu'elle se déplace à nouveau. Si j’estime un vecteur ou essaie d’interpoler, j’ai un problème car je n’enregistre pas l’état précédent. Le moteur physique n’a aucun moyen de dire "vous devez vous arrêter après avoir atteint la position X". Il comprend simplement une vitesse, rien de plus complexe. Je suis réticent à l'idée d'ajouter les informations "état de mouvement du paquet" aux entités ou au moteur physique, car elles enfreignent les principes de conception de base et saignent le code de réseau dans le reste du moteur de jeu.

  4. Que devrait-il se passer lorsque des entités entrent en collision? Il existe trois scénarios: le joueur contrôlant se heurte localement, deux entités se heurtent sur le serveur lors d'une mise à jour de position ou une mise à jour d'entité distante se heurte sur le client local. Dans tous les cas, je ne sais pas comment gérer la collision - mis à part la triche, les deux états sont "corrects" mais à des périodes différentes. Dans le cas d’une entité distante, il n’a pas de sens de la dessiner en traversant un mur. C’est pourquoi je détecte les collisions sur le client local et le "stoppe". Sur la base du point 2 ci-dessus, je pourrais calculer un "vecteur corrigé" qui tente continuellement de déplacer l'entité "à travers le mur", ce qui ne réussira jamais - l'avatar distant est bloqué là jusqu'à ce que l'erreur devienne trop importante et qu'il "s'enclenche" position. Comment les jeux fonctionnent-ils autour de cela?

Chasseur d'ombre
la source
1
Qu'est-ce qu'un jeu en 3D ou en 2D a à voir avec le type de serveur que vous utilisez? et pourquoi un serveur athoritif ne fonctionne-t-il pas pour votre jeu?
AttaquerHobo le
1
@ Roy T. compromis de bande passante. La bande passante est la ressource la plus précieuse des systèmes informatiques actuels.
FxIII
1
C'est tout simplement faux, les jeux en ligne sont largement dominés par le temps de réponse. Par exemple, sur une ligne à 10 Mbits (1,25 Mo / s), le temps de latence entre le serveur et le client est de 20 ms, l'envoi d'un paquet de 1,25 Ko nécessitant 20 ms + 1 ms. L'envoi d'un paquet de 12,5 Ko prendra 30 ms. Sur une ligne deux fois plus rapide, un paquet de 1,25 Ko prendra toujours 20 ms + 0,5 ms et 20 ms + 5 ms pour le paquet de 12 Ko. La latence est le facteur limitant, pas la bande passante. Quoi qu'il en soit, je ne sais pas combien de données il y a, mais l'envoi de 50 vecteurs3 (25x position + 25x rotation) n'est que de 600 octets, l'envoi de cette valeur toutes les 20 ms coûtera 30 kb / s. (+ paquet overhead).
Roy T.
2
Le moteur Quake a une prédiction depuis la première version. La prévision de tremblement de terre est décrite ici et à d'autres endroits. Vérifiez-le.
user712092
1
Faites-vous cette position + = vélocité * deltatime pour chaque entité en parallèle (impérativement: en code, vous avez 2 tableaux de paramètres physiques d’entités, séparés d’un cadre, vous mettez à jour l’ancienne pour la rendre plus récente et les permutez)? Sean Barret, qui a créé le moteur Thief 1, pose quelques problèmes .
user712092

Réponses:

3

La seule chose à dire est que 2D, isométrique, 3D, ils sont tous identiques quand il s'agit de ce problème. Parce que vous voyez de nombreux exemples en 3D et que vous utilisez uniquement un système de saisie 2D limité en octants avec une vitesse instantanée, cela ne signifie pas que vous pouvez vous débarrasser des principes de mise en réseau qui ont évolué au cours des 20 dernières années.

Les principes de conception doivent être damnés lorsque le jeu est compromis!

En éliminant les anciens et les actuels, vous vous débarrassez des quelques informations pouvant résoudre votre problème. J'ajouterais à ces données des horodatages et des décalages calculés afin que l'extrapolation puisse mieux prédire l'emplacement de ce joueur et que l'interpolation puisse mieux lisser les changements de vitesse dans le temps.

Ce qui précède est l'une des principales raisons pour lesquelles les serveurs semblent envoyer beaucoup d'informations d'état et non des entrées de contrôle. Une autre grande raison est basée sur le protocole que vous utilisez. UDP avec perte de paquets acceptée et livraison hors commande? TCP avec livraison assurée et tentatives? Quel que soit le protocole utilisé, vous obtiendrez des paquets à des moments bizarres, en retard ou empilés les uns sur les autres lors d'une vague d'activité. Tous ces paquets étranges doivent s'inscrire dans un contexte afin que le client puisse comprendre ce qui se passe.

Enfin, même si vos entrées sont très limitées dans 8 directions, le changement peut se produire à tout moment - imposer un cycle de 250 ms frustrera simplement les joueurs rapides. 30 joueurs n’ont rien de gros à gérer pour aucun serveur. Si vous parlez de milliers ... alors même que des groupes sont répartis sur plusieurs boxen, les serveurs individuels ne supportent qu'une charge raisonnable.

Avez-vous déjà profilé un moteur physique comme Havok ou Bullet en marche? Ils sont vraiment très optimisés et très, très rapides. Vous risquez de tomber dans le piège de supposer que l'opération ABC sera lente et d'optimiser quelque chose qui n'en a pas besoin.

Patrick Hughes
la source
Sage-conseil ici! Il est facile de perdre de vue la vue d'ensemble. J'utilise TCP dans ce cas. Le problème des "8 directions" ne pose pas autant de problèmes en termes d'entrées, mais plutôt d'interpolation et d'extrapolation. Les graphismes sont limités à ces angles et utilisent des sprites animés - le gameplay "a l'air bizarre" si le joueur se déplace dans un angle ou une vitesse différents, ce qui est trop éloigné de la norme.
ShadowChaser
1

Donc, votre serveur est essentiellement un "arbitre"? Dans ce cas, je pense que tout dans votre client doit être déterministe; vous devez vous assurer que tout sur chaque client donnera toujours le même résultat.

Pour votre première question, une fois que le joueur local a reçu la direction des autres joueurs, mis à part le fait de pouvoir déceler son mouvement au fil du temps et d’appliquer des collisions, je ne vois pas comment vous pourriez prédire dans quelle direction le joueur va tourner, en particulier dans un 8 environnement de direction.

Lorsque vous recevez la mise à jour "de la position réelle" de chaque joueur (que vous pourriez peut-être essayer d’échouer sur le serveur), vous devrez interpoler la position et la direction du joueur. Si la position "supposée" est très fausse (le joueur a complètement changé de direction juste après l'envoi du dernier paquet de direction), vous aurez un énorme vide. Cela signifie que le joueur saute de position ou que vous pouvez interpoler vers la position estimée suivante . Cela permettra une interpolation plus douce dans le temps.

Lorsque des entités entrent en collision, si vous pouvez créer un système déterministe, chaque joueur peut simuler la collision localement et les résultats ne doivent pas être trop éloignés de la réalité. Chaque machine locale doit simuler la collision pour les deux joueurs, auquel cas il faut s’assurer que l’état final sera non bloquant et acceptable.

Pour le côté serveur, un serveur d’arbitre peut toujours effectuer des calculs simples pour vérifier, par exemple, la vitesse d’un joueur sur de courtes périodes, pour l’utiliser comme un simple mécanisme anti-fraude. Si vous surveillez chaque joueur plusieurs fois à la fois, votre détection de triche sera évolutive, mais il faudra plus de temps pour trouver des tricheurs.

Jonathan Connell
la source
Merci - cela semble assez proche de ce dont j'ai besoin, en particulier du côté serveur. Un point intéressant est que, bien que les joueurs soient verrouillés dans 8 directions, le mouvement interne est un vecteur 3D. J'y ai pensé un peu plus la journée passée et je pense que le fait de ne pas avoir d'interpolation implémentée est un problème. J'utilise simplement une intégration très basique, le réglage de la vitesse et la mise à jour de la position vectoriser chaque mise à jour.
ShadowChaser
Je ne sais pas comment combiner cela avec une interpolation ou une prédiction. J'ai essayé de prendre la position mise à jour envoyée dans le paquet, de l'intégrer sur une période de temps déterminée (par exemple 200 ms), puis de déterminer le vecteur (vitesse) nécessaire pour atteindre ce point dans 200 ms. En d'autres termes, quelle que soit la position incorrecte actuelle du joueur côté client, il doit toujours atteindre la même "position correcte estimée" en 200ms. Cela a fini par envoyer mon personnage dans des directions complètement folles - je suppose, car les 200 ms devraient vraiment être l'heure du prochain paquet, ce que je ne peux pas estimer.
ShadowChaser
Vous êtes-vous assuré d’abord d’intégrer la bonne position de t à t + 1 avant d’intégrer la mauvaise position à la bonne position devinée à t + 1?
Jonathan Connell
Oui, j'ai vérifié deux fois que j'utilisais la bonne position pour l'intégration d'origine. À l’origine, c’était un bug, mais sa résolution ne semblait toujours pas créer d’amélioration notable. Mon soupçon est le "+1" - il doit être très dépendant du temps entre les paquets. Il y a deux problèmes: envoyer les changements d'état en plus des mises à jour régulières (250 ms) et je ne peux pas prédire quand cela se produira. De plus, je suis réticent à verrouiller un intervalle spécifique, car il est logique que le serveur envoie moins de mises à jour pour les entités plus éloignées du lecteur. Le temps entre les paquets peut changer.
ShadowChaser
1
Ouais, y compris un type de timestep fixe n'est probablement pas une bonne idée. Je crains cependant que le caractère erratique du mouvement dans 8 directions soit très difficile (sinon impossible?) À prédire. Même dans ce cas, vous pourrez peut-être utiliser la latence moyenne du client pour prédire t + 1 et définir un seuil au-dessus duquel vous "téléportez" toujours les autres joueurs vers leurs nouvelles positions.
Jonathan Connell le
0

Ne pouvez-vous pas inclure la vélocité dans vos messages de changement d'état et l'utiliser pour prédire le mouvement? Par exemple, supposez que la vitesse ne change pas jusqu'à ce que vous obteniez un message disant que cela a changé? Je pense que vous envoyez déjà des positions, donc si quelque chose "dépasse" à cause de cela, vous avez de toute façon la bonne position de la prochaine mise à jour. Vous pouvez ensuite modifier les positions lors des mises à jour, comme vous le faites déjà, en utilisant la vélocité du dernier message et en écrasant la position chaque fois qu'un message est reçu avec une nouvelle position. Cela signifie également que si la position ne change pas, mais que la vélocité nécessite l'envoi d'un message (même si cela reste un cas valable dans votre jeu), cela n'affectera pas beaucoup votre consommation de bande passante, voire pas du tout.

L'interpolation ne devrait pas avoir d'importance ici, c'est-à-dire, par exemple, quand vous savez où quelque chose se trouvera à l'avenir, si vous l'avez, quelle méthode vous utilisez, etc. Êtes-vous confondu avec l'extrapolation peut-être? (pour lequel ce que je décris est un, simple, approche)

Jheriko
la source
-1

Ma première question serait: qu'est-ce qui ne va pas avec un modèle où le serveur a autorité? Pourquoi est-il important que l'environnement soit 2D ou 3D? Si votre serveur faisait autorité, cela faciliterait beaucoup votre protection contre les tricheurs.

La plupart des échantillons que j'ai vus associent étroitement la prédiction de mouvement aux entités mêmes. Par exemple, stocker l'état précédent avec l'état actuel. J'aimerais éviter cela et ne conserver que les entités avec leur "état actuel". Y a-t-il une meilleure façon de gérer cela?

Lors de la prédiction, il est nécessaire de conserver plusieurs états (ou au moins des deltas) sur le client afin que, lorsque le / les état (s) faisant autorité est reçu du serveur, il puisse être comparé à ceux du client et vous permet de prendre les mesures nécessaires. corrections. L'idée est de garder autant que possible déterministe afin de minimiser le nombre de corrections nécessaires. Si vous ne conservez pas les états précédents, vous ne pouvez pas savoir si quelque chose de différent s'est passé sur le serveur.

Que devrait-il se passer lorsque le joueur s’arrête? Je ne peux pas interpoler à la position correcte, car ils pourraient avoir besoin de marcher en arrière ou dans une autre direction étrange si leur position est trop éloignée.

Pourquoi avez-vous besoin d'interpoler? Le serveur faisant autorité doit annuler tout mouvement erroné.

Que devrait-il se passer lorsque des entités entrent en collision? Si le joueur actuel entre en collision avec quelque chose, la réponse est simple: il suffit d'empêcher le joueur de bouger. Mais que se passe-t-il si deux entités occupent le même espace sur le serveur? Que se passe-t-il si la prédiction locale provoque une collision entre une entité distante et le joueur ou une autre entité? Est-ce que je les arrête également? Si la prédiction avait le malheur de les coller devant un mur que le joueur a contourné, elle ne pourra jamais compenser et une fois que l'erreur atteindra un niveau élevé, l'entité se positionnera à la nouvelle position.

Il existe des situations conflictuelles entre le serveur et le client, raison pour laquelle vous devez conserver des états sur le client afin que le serveur puisse corriger les erreurs éventuelles.

Désolé pour les réponses rapides, je dois partir. Lisez cet article , il mentionne les tireurs mais devrait fonctionner pour tout jeu nécessitant une mise en réseau en temps réel.

Gyan aka Gary Buyn
la source
Quelques réponses: * Si le serveur a autorité, il serait responsable du suivi de toutes les entités en mouvement et de la mise à jour de leurs positions à intervalles réguliers. En d’autres termes, il doit exécuter le moteur physique - ce qui pourrait coûter cher. L’évolutivité est l’un de mes principaux objectifs de conception. * J'ai besoin d'interpoler côté client, sinon chaque mise à jour de serveur envoyée aux clients fera sauter les entités. En ce moment, mon interpolation est effectuée dans le moteur physique - il ne fait que définir la vitesse. Il n'y a pas d'états ou de deltas.
ShadowChaser
J'ai lu tous les articles de Glenn, mais il déclare dans ses commentaires qu'ils sont uniquement destinés aux tireurs (fréquences de mise à jour élevées). Quelques-uns de ses articles parlent de clients autoritaires, ce qui est la mise en œuvre pour laquelle je m'efforce. Je ne veux pas faire d'interpolation / physique sur le serveur, mais je suis disposé à changer d'avis si c'est vraiment le seul moyen :)
ShadowChaser Le
1
-1. Ce que vous avez écrit n'aborde que vaguement le sujet. se sent ambigu. Les réponses se sentent sous-égales lorsqu'elles sont essentiellement "lues ce long article", sans toutefois contenir des informations utiles tirées de l'article en question.
AttaquerHobo le
1
@ AttackingHobo, je serais d'accord avec vous. J'ai mentionné que j'étais pressé mais ce n'est pas une excuse. Si je n'avais pas eu le temps, il aurait été préférable de le laisser seul. Leçon apprise.
Gyan, alias Gary Buyn, le