Interpolation de positions dans un jeu multijoueur

14

Afin d'économiser de la bande passante dans mon jeu multijoueur , je ne mets pas à jour chaque objet à chaque tick du serveur, mais chaque objet a un updateRate qui indique au jeu que cet objet devrait être mis à jour à chaque tick du serveur X.

Lorsque je reçois un message de mise à jour pour un objet, je calcule l'heure à laquelle j'attends la prochaine mise à jour:

origin = serverCurrentPosition
diff = serverNextPosition - origin
arriveTime = now + timeBetweenTicks * updateRate

Lorsque je dessine l'objet, je calcule le temps restant jusqu'à la prochaine mise à jour et interpole la position en conséquence:

step = 100 / timeBetweenTicks * updateRate
delta = 1 - step * ((arriveTime - now) / 100)
position = origin + diff * delta

Cela fonctionne ... mais il y a encore un peu de gigue dans le dessin, bien que dans ma théorie, tout devrait fonctionner correctement, car la mise à l'échelle devrait prendre en charge un certain décalage, non?

Donc la question ici est, est-ce la meilleure approche? Dois-je mettre un retard réel dans le calcul? Si oui, comment ferais-je cela? J'ai fait quelques expériences, mais la gigue n'a fait qu'empirer.

Ivo Wetzel
la source
Salut Ivo. Je pense que c'est un bon sujet, mais ce que fait votre code n'est pas clair - par exemple, d'où viennent serverCurrentPosition, serverNextPosition, timeBetweenTicks?
CiscoIPPhone
C'est envoyer les données de mise à jour qui proviennent du serveur.
Ivo Wetzel,

Réponses:

11

Vous avez de la gigue, car votre décalage est en constante évolution. Cela signifie que, bien que le serveur envoie des mises à jour exactement à chaque timeBetweenTickstick, le client les reçoit après un certain temps variable. Ce temps est probablement proche d' timeBetweenTicksune bonne connexion, mais pas exactement égal (et d'ailleurs, vous pouvez avoir un décalage de serveur et des vitesses d'horloge différentes sur le serveur et le client).

Ainsi, lorsque vous comptez recevoir la mise à jour exactement à l'heure spécifiée, vous arrivez constamment à destination un peu avant / après la mise à jour réelle. Par conséquent, la gigue.

Une approche simple pour réduire la gigue consiste à utiliser des «élastiques», ce que Martin suggère dans une autre réponse. Fondamentalement, lorsque vous recevez une mise à jour, vous ne changez pas immédiatement la position de l'objet. Au lieu de cela, si la position du client et la position du serveur ne diffèrent que légèrement, vous commencez à interpoler la position du client, de sorte qu'après un certain temps (par exemple, à mi-chemin de la prochaine mise à jour), les positions du client et du serveur convergent.

Une autre idée pour réduire la gigue dans votre configuration: puisque vous transmettez les coordonnées "actuelles" et "suivantes", vous pouvez calculer la vitesse de l'objet. Ensuite, lorsque la mise à jour est en retard, vous n'arrêtez pas l'objet à sa destination (c'est-à-dire la position "suivante"), mais continuez à le déplacer avec la même vitesse. Si vos objets ne changent pas brusquement de vitesse, cela améliorera vraiment la fluidité du mouvement sur le client.

Ça ne fait rien
la source
C'est déjà ainsi, que les objets ne s'arrêtent pas, ils continuent à avancer jusqu'à la prochaine mise à jour. De plus, l'utilisation de l'accélération matérielle sur le canevas HTML semble réduire considérablement l'effet de gigue. Peut-être que je suis devenu fou après avoir travaillé sur cette chose pendant si longtemps.
Ivo Wetzel
C'est tout à fait possible. Si l'activation de l'accélération augmente la fréquence d'images, la probabilité de gérer les informations de mise à jour exactement au bon moment augmente.
Nevermind
9

J'ai résolu ce problème auparavant avec un certain succès avec une approche que j'appelle "ombres réseau". Je ne sais pas si c'est quelque chose que font les autres, mais ça a toujours fonctionné pour moi.

Chaque entité qui est synchronisée sur le réseau a une entité invisible de réseau invisible. Lorsqu'une mise à jour arrive du réseau, vous téléportez l'ombre directement à la position où le réseau devrait se trouver, puis vous interpolez lentement l'entité visible locale vers l'ombre au fil du temps.

J'ai inclus beaucoup de détails sur cette approche dans ma réponse précédente ici

Martin
la source
Hm, j'ai fait quelque chose comme ça dans une version antérieure, l'imprécision en virgule flottante l'a rendu vraiment mauvais parfois, j'ai des intervalles de temps assez grands entre les mises à jour, jusqu'à 300 ms pour certains objets, mais peut-être que je l'ai mal fait, je vais le donner un coup quand je trouve du temps libre :)
Ivo Wetzel
la précision en virgule flottante ne devrait vraiment pas entrer en jeu du tout! Avez-vous lu ma réponse liée sur stackoverflow? Il couvre tous les détails de la mise en œuvre de ce genre de chose.
Martin