¿Cómo sincronizar relojes en el juego multijugador?

8

Tengo de 2 a 3 clientes que pueden intercambiar mensajes a través de Apple Game Center.

La única sincronización que necesito es: iniciar el juego en el mismo momento.

Supongo que esto implica la sincronización del reloj. ¿Cómo lograr esto?

GameCoder
fuente
3
La respuesta corta es 'no puedes'. El retraso inevitable (e inconsistente) en las comunicaciones entre dispositivos significa que incluso si un dispositivo dice 'Creo que ahora son las 12: 00: 00.00', podría ser fácilmente entre las 12: 00: 00.10 y las 12:00:05 por el hora en que se recibe ese mensaje en otro lugar. En un modelo basado en servidor, no puede hacer mucho mejor que simplemente hacer que su servidor envíe el mensaje 'Iniciar juego' a los clientes al mismo tiempo.
Steven Stadnicki
Bien que no necesito perfección. ¿Cómo hacerlo mejor que nada? Tal vez: cada jugador envía el mensaje "INICIO" y comienza tan pronto como recibe el mensaje INICIO de todos los demás jugadores
GameCoder
1
@StevenStadnicki Los retrasos en la red no lo hacen imposible. El problema general se llama "sincronización de reloj" y está bien estudiado. (Sin embargo, si solo controla los puntos finales, entonces no es posible tener en cuenta la asimetría de los retrasos.)
Praxeolitic
Puedo estar malinterpretando su problema, pero ¿no puede hacer que el mensaje que envía a los clientes indique una hora de inicio un poco en el futuro? Si es el momento nowy envía un mensaje para comenzar exactamente en now + halfSecondese momento, siempre y cuando todos reciban el mensaje dentro de medio segundo, y mientras sus relojes del sistema estén sincronizados correctamente, todos comenzarán al mismo tiempo.
Mark

Respuestas:

12

El comentario de Steven es correcto: esto es teóricamente imposible de hacer.

Afortunadamente, en la práctica puedes acercarte, que es cómo funcionan cosas como NTP .

Por ejemplo, mejor que simplemente enviar un mensaje a 3 clientes diciendo "comenzar ahora", puede intercambiar un par de mensajes de ping de antemano para medir el tiempo que lleva enviar un mensaje al cliente, y cuando envía el mensaje de inicio, en lugar de "comenzar ahora" diga "comenzar en X milisegundos" y ajuste X para los diferentes tiempos que tarda un mensaje en llegar.

p.ej.:

  • Envía un mensaje al Cliente 1 y recibe una respuesta 20ms más tarde. Supone que se necesitan aproximadamente 10 ms para enviar un mensaje al cliente 1.
  • Usted hace lo mismo para el Cliente 2 y obtiene una respuesta 28 ms más tarde, por lo que es probable que el tiempo de transmisión sea de aproximadamente 14 ms.
  • Entonces, envía un mensaje al Cliente 1 que dice "comienza el juego en 50 ms" y envía uno al Cliente 2 que dice "comienza el juego en 46 ms". El cliente 2 recibirá el mensaje unos 4 ms más tarde, pero esperará 4 ms menos antes de comenzar el juego.

Esto no puede garantizar la sincronización porque el tiempo necesario para enviar un mensaje a través de Internet varía, y porque puede ser diferente en cada dirección. El primero puede reducir los efectos realizando la medición varias veces y tomando una lectura mediana. El segundo es más complicado y puede ser teóricamente imposible de resolver (aunque no puedo recordar la prueba en este momento). La buena noticia es que probablemente no necesites tanta precisión.

Kylotan
fuente
Probablemente necesitaré limitar la actividad de mis aplicaciones durante la sincronización; en este momento doy algo de tiempo de CPU al subsistema de red en cada marco de dibujo. Si la velocidad de fotogramas es de 30 FPS, responderé el ping en 1/30 = 33 ms de todos modos. También podría agregar los 33 ms, o la diferencia entre el "tiempo de recepción de la pila TCP" y mi MilisecondTicks actual () ..
GameCoder
2
Si esperas mantener los sistemas sincronizados con precisión cuadro por cuadro durante el juego, creo que es un juego perdedor y deberías rendirte ahora. Los relojes y la latencia se desviarán y la información siempre tomará tiempo en viajar. Independientemente de lo que intente hacer, probablemente no necesite este nivel de sincronización.
Kylotan
Creo que nos estamos perdiendo el punto. No es la perfección lo que me molesta, es la experiencia del usuario. Esto puede ser bueno o mejor, y me gustaría lo mejor. Eso es.
GameCoder
2
Una buena experiencia de usuario no requiere una sincronización precisa. Es por eso que la mayoría de los juegos no lo intentan.
Kylotan
1
Es teóricamente imposible si cada jugador controla su propio bate, porque lleva una cantidad de tiempo diferente a cero para que esa información llegue a la otra computadora, durante la cual los 2 lados tienen información diferente. En su lugar, encuentra formas de hacer frente a las diferencias. Su problema no es realmente diferente de cualquier otro juego en línea de ritmo rápido y hay varias otras preguntas sobre cómo manejar las redes en este sitio. Uno está aquí: gamedev.stackexchange.com/questions/22444/…
Kylotan
3

Como se mencionó, esto es imposible, así que probaría otro enfoque:

Si no tiene un servidor dedicado, elija un cliente participante para que se convierta en el host (esto se puede transferir si es necesario).

El anfitrión ahora realizará toda la lógica importante del juego, como detección de golpes, controles de IA, manejo de inventario, etc., así como el seguimiento del tiempo (es decir, dictar el tiempo del juego).

Los otros clientes solo intentarán mantenerse sincronizados con el host, tratando de estimar o aproximar el valor esperado. Si el retraso aumenta o hay una pérdida de paquetes, las cosas pueden volverse entrecortadas, pero es trivial ponerse al día, esencialmente esperando la próxima actualización.

La mayoría de los juegos (especialmente FPS) ocultan este hecho al hacer su propio cálculo local para el propio movimiento del jugador, disparos, etc. para evitar que el juego se sienta lento. Todo aún se corrige en función de los datos del servidor. Esto puede generar cierta confusión, por ejemplo, te ves disparando al enemigo, pero en el mismo momento que caes muerto (sin que el enemigo reciba un golpe), pero sigue siendo un enfoque mucho mejor que la sincronización completa.


Si aún insiste en mantener todo sincronizado, es probable que desee crear algún tipo de paso o contador de trama, para que todos los clientes solo procesen un paso lógico, luego sincronicen sus datos, etc. Tenga en cuenta que esto puede ser ancho de banda intensivo y lento, por lo que no recomendaría hacerlo a menos que tenga muchos datos de otra manera y su juego esté basado en turnos (por ejemplo, juegos de estilo Artillery / Worms).

Mario
fuente
1

Recomiendo sincronizar los temporizadores del sistema en todos los clientes y el servidor mediante el protocolo NTP [Stratum 2], luego el servidor envía un comando para iniciar el juego a la hora especificada, digamos, cuando todos los temporizadores alcanzan las 0:05:00. Este enfoque debería darle una sincronización precisa de 3-4 ms, creo.

ivan866
fuente