Je travaille actuellement sur un jeu de plateforme multijoueur plutôt simple. J'ai lu pas mal d'articles sur les techniques utilisées pour masquer la latence, mais je n'arrive toujours pas à comprendre certains des concepts. Je trouve le sujet très intéressant et j'aime essayer des idées par moi-même, mais je pense que demander gamedev stackexchange sera plus efficace pour ma question. Je ferai de mon mieux pour décrire ma situation actuelle et quelle question s'est posée en cours de route.
Pour le moment, je veux seulement synchroniser un seul joueur avec le serveur. Théoriquement, j'ai supposé qu'un joueur avec une prédiction côté client ne nécessiterait pas de corrections du serveur, car il n'y a pas de facteurs externes qui influencent son mouvement. Par conséquent, mon prototype n'a actuellement qu'un seul joueur synchronisé avec un serveur sans que des corrections de serveur ne soient envoyées.
Si vous êtes familier avec la mise en réseau de jeux, je pense que vous pouvez sauter les sections contextuelles, même si j'ai peut-être fait quelque chose de mal en cours de route.
La boucle client (une fois par trame, une fois tous les ~ 16,67 ms)
La boucle client simplifiée ressemble à:
Vérifiez l'entrée locale (WASD) et empaquetez-les sous forme d'actions (par exemple
Type=MoveLeft, Time=132.0902, ID=15
). Nous conservons les actions packagées pour éventuellement les envoyer plus tard. De plus, nous appliquons directement l'action souhaitée à la simulation physique locale du jeu. Par exemple, si nous avons uneMoveLeft
action, nous appliquons une force vers la gauche sur la vitesse du joueur.Cochez pour envoyer des actions. Pour éviter d'abuser de la bande passante du client, envoyez uniquement les actions packagées à certains intervalles (par exemple 30 ms).
Appliquer les modifications du serveur. À un certain moment, cela gérera les deltas et les corrections reçues par le serveur et les appliquera à la simulation locale du jeu. Pour cette question particulière, ce n'est pas utilisé.
Mettez à jour la physique locale. Exécutez la boucle physique sur le lecteur principal. Fondamentalement, cela fait la prédiction côté client du mouvement du joueur. Cela ajoute de la gravité à la vitesse du joueur, applique la vitesse du joueur à sa position, corrige les collisions en cours de route, etc. Je dois préciser que la simulation physique est toujours exécutée avec des secondes delta fixes (appelées plusieurs fois en fonction des secondes delta réelles) .
Je saute quelques détails spécifiques sur la physique et d'autres sections parce que je pense qu'ils ne sont pas requis pour la question, mais n'hésitez pas à me faire savoir s'ils seraient pertinents pour la question.
La boucle du serveur (toutes les 15 ms)
La boucle de serveur simplifiée ressemble à:
Gérer les actions. Vérifiez les packages d'actions reçus des clients et appliquez-les à la simulation physique du serveur. Par exemple, nous pourrions recevoir 5
MoveLeft
actions, et nous appliquerions la force à la vitesse 5 fois . Il est important de noter qu'un package d'action complet est exécuté sur une "trame" , contrairement au client où il est appliqué dès que l'action se produit.Mettez à jour la logique du jeu. Nous mettons à jour la physique du jeu, déplaçant les joueurs et corrigeant les collisions, etc. Nous emballons également tous les événements importants qui ont été envoyés aux joueurs (par exemple, la santé d'un joueur a chuté, un joueur est décédé, etc.) plus tard.
Envoyer des corrections. Nous envoyons régulièrement (par exemple une fois tous les 35 ms) des deltas à d'autres joueurs (par exemple, la position des joueurs, leur santé, etc.) s'ils ont récemment changé. Cette partie n'est pas actuellement implémentée, car je veux que la simulation d'un seul joueur donne les mêmes résultats sur le client et le serveur sans corrections, pour s'assurer que la prédiction côté client fonctionne correctement.
Le problème
Le système actuel fonctionne bien dans des circonstances simples, et j'ai été heureusement surpris de voir qu'il a donné des résultats très similaires avec de simples mouvements horizontaux (les inexactitudes sont dues à des erreurs de précision en virgule flottante, je crois):
Veuillez ignorer les graphiques du prototype. Rectangle blanc = joueur, rectangles rouges = obstacles, bleu = fond
Cependant, je reçois des erreurs de synchronisation après avoir effectué des mouvements sensibles au temps, tels que sauter et se rapprocher d'un obstacle isolé:
En théorie, je m'attendrais à ce que les deux se retrouvent toujours avec les mêmes résultats, car aucun facteur externe n'influence le client sur sa position. Dans la pratique, cependant, je pense que je comprends le problème.
Étant donné que sauter autour d'un obstacle comme celui-ci dépend beaucoup du timing du joueur, de petites variations du moment où la vitesse est appliquée à la position auront des répercussions sur le résultat (par exemple, le client pourrait s'éloigner juste à temps pour éviter une collision avec le obstacle, alors que le serveur le ferait en recevant le package d'action plus tard et en restant coincé sur l'obstacle pendant un petit laps de temps, en modifiant le résultat final). La différence entre la façon dont le client et le serveur le gèrent réside principalement dans le fait que le client effectue toutes ses actions au fur et à mesure qu'elles se produisent, tandis que le serveur les fait toutes en bloc lorsqu'il les reçoit.
La question
Ce long contexte mène finalement à ma question (merci d'avoir lu jusqu'ici): est-il normal d'exiger des corrections de serveur même lorsqu'il n'y a qu'un seul joueur synchronisé avec le serveur, ou dois-je utiliser certaines techniques pour éviter la désynchronisation dans des situations sensibles au facteur temps ?
J'ai pensé à certaines solutions possibles, dont certaines me mettent moins à l'aise:
Implémentez la correction du serveur. Supposez simplement qu'il s'agit d'un comportement normal et corrigez les erreurs à mesure qu'elles se produisent. Je voulais quand même le mettre en œuvre, mais je voulais juste m'assurer que ce que j'ai fait jusqu'à présent est acceptable.
Utilisez le temps client fourni pour appliquer les actions souhaitées. Je suppose que cela serait similaire à la compensation de décalage, nécessitant de "remonter le temps" et de vérifier les mouvements. Un peu comme appliquer des corrections de serveur, remonter dans le temps et réappliquer les actions suivantes après cela. Je n'aime vraiment pas l'idée. Il semble complexe, coûteux en ressources et nécessite de faire confiance au temps donné au client (même si je prévois vraiment de vérifier que le temps semble relativement légitime).
Demandez à GameDevelopment StackExchange une excellente nouvelle idée qui résoudra tous mes problèmes.
Je ne fais que commencer dans le monde du jeu en réseau, alors n'hésitez pas à corriger / critiquer / insulter l'un des concepts ci-dessus ou à donner des idées / ressources qui pourraient m'aider tout au long de mon voyage dans le monde merveilleux du réseautage. Pardonnez-moi si j'aurais pu trouver ma réponse ailleurs, j'ai échoué.
Merci beaucoup pour votre précieux temps.