¿Ejecutar una simulación física tanto en el cliente como en el servidor?


13

Estoy implementando un clon de asteroides multijugador para aprender sobre la arquitectura de red cliente / servidor en los juegos. He pasado tiempo leyendo las publicaciones de GafferOnGames y Valve sobre la tecnología de su cliente / servidor. Tengo problemas con dos conceptos.

  1. Actualmente tengo un servidor de juegos autorizado que simula la física con box2d y envía el estado del mundo a los clientes aproximadamente 20 veces por segundo. Cada cliente realiza un seguimiento de las últimas instantáneas que recibió y salta entre dos estados para suavizar el movimiento de los sprites. Sin embargo, no es tan suave. Puede ser suave por un tiempo, luego un poco irregular, luego volver a ser suave, etc. He intentado tanto TCP como UDP, ambos son casi lo mismo. ¿Alguna idea de cuál podría ser mi problema? (Nota: Primero implementé esto para un solo jugador, y el movimiento del sprite es perfectamente suave a 60 fps cuando actualizo el mundo de la física solo 20 veces por segundo).

  2. Para resolver el primer problema, pensé que tal vez el cliente también debería ejecutar una simulación box2d y simplemente actualizar las posiciones de sus sprites para que coincidan con las instantáneas del servidor cuando no coinciden. Pensé que esto podría ser más sencillo ya que mi implementación para un solo jugador es fluida. ¿Es esta una buena idea?

    Incluso si no soluciona el problema anterior, ¿es necesario para la predicción del lado del cliente? Por ejemplo, si un jugador intenta mover su nave, ¿cómo sabrán si chocan contra un asteroide, una pared o una nave enemiga sin una simulación física? Parece que su nave parece pasar a través del objeto con el que debería colisionar antes de recibir una instantánea del servidor que dice que golpeó el objeto.

¡Gracias!

Respuestas:


10

Definitivamente ejecute la simulación tanto en los clientes como en el servidor. Cualquier otra cosa tiene una latencia demasiado larga. Debe asegurarse de que las simulaciones coinciden insertando objetos en el mismo orden, utilizando un paso de tiempo fijo y evitando la comparación de punteros. No he intentado esto con Box2D, pero en general es posible lograr el mismo comportamiento en todas las máquinas en una simulación física. Todas las matemáticas generalmente se basan en flotantes binarios IEEE 754 binary32 y su comportamiento está estrictamente definido para operaciones como +-*/nombrar algunas. Es necesario tener cuidado con sin,cosy los gustos difíciles, ya que pueden diferir entre tiempos de ejecución (esto es especialmente importante cuando se desarrolla para múltiples plataformas). Además, asegúrese de utilizar una configuración estricta para las optimizaciones flotantes en su compilador. Todavía puede sincronizar objetos enviando periódicamente el estado de los objetos desde el servidor. No actualice a menos que la diferencia sea mayor que un umbral para evitar la tartamudez innecesaria.

Un tema que viene a la mente es la creación de nuevos objetos y cómo eso cambiará la simulación entre clientes. Una forma de solucionar esto es dejar que el servidor cree todos los objetos. Si el paso de tiempo actual es t, el servidor programará un objeto para agregarse en t+d. Por lo tanto, una lista de objetos nuevos, con objetos para agregar y cuándo agregarlos, se puede mantener en todos los clientes y el servidor puede actualizar con mucha anticipación. Si des lo suficientemente grande, minimiza el riesgo de resultados diferentes. Si realmente no puede manejar la diferencia, puede obligar a un cliente a esperar información sobre nuevos objetos durante un cierto paso de tiempo antes de simular ese paso de tiempo.


Gracias por su respuesta. No creo que box2d esté garantizado para generar los mismos resultados en diferentes CPU, que sería el escenario para nosotros ya que estamos escribiendo un juego de escritorio. Espero que las diferencias sean mínimas y fácilmente corregibles con actualizaciones periódicas de un servidor autorizado, pero nunca lo he intentado.
Venesectrix

Erin Catto piensa que tratar de mantener todo el estado de múltiples mundos Box2D sincronizados es una batalla perdida ( box2d.org/forum/viewtopic.php?f=3&t=8462 )
Pavel

La afirmación "+-*/ El comportamiento de IEEE 754 binary32 floats [..] está estrictamente definido para operaciones como " es completamente falsa. Todas esas operaciones en IEEE-754 podrían variar según la implementación. Vea aquí y aquí para más información.
BlueRaja - Danny Pflughoeft

1
No. Es completamente cierto. El problema que describe su enlace está relacionado con los diferentes modos de la x87 fpu y las implementaciones de trascendentales. IEEE 754 binary32 está estrictamente definido para las operaciones básicas. Depende de usted establecer los modos correctos y utilizar las instrucciones correctas para que se siga el estándar. Simplemente usando las instrucciones SSE y no la x87 fpu ayuda mucho.
rasmus

4

Probablemente no se vea tan bien ya que la interpolación entre ellos depende de tener siempre el siguiente conjunto de datos para interpolar. Esto significa que, si hay un pico de retraso corto, todo tiene que esperar para ponerse al día.

Hay un artículo antiguo en GameDev sobre el uso de splines cúbicos para predecir la posición de un objeto más allá del punto donde tuvo los últimos datos. Lo que debe hacer es usar esa posición y luego ajustar la spline cuando obtenga nuevos datos para contabilizar su nueva posición. También es probablemente mucho más barato que ejecutar una segunda simulación de física, y significa que no tiene que decidir en quién confía, ya que ha implementado explícitamente el cliente que lo inventa a medida que avanza. :)


Este podría ser el caso. Lo que intento hacer es retrasar hasta que haya recibido 3 instantáneas del servidor. En el punto, paso del tiro 1 al tiro 2. Luego, del tiro 2 al tiro 3. Si en algún momento pierdo un paquete, puedo saltar del 1 al 3, en lugar del 1 al 2, si eso tiene sentido. Sin embargo, es posible que todavía no esté implementando esto correctamente. Gracias por el enlace al artículo!
Venesectrix

1

He hecho algunas cosas similares yo mismo, y ejecuté Box2D solo en los clientes. La forma en que lo hice fue dejar que el cliente ejecutara su propia simulación prácticamente por sí mismo, enviando la velocidad actual (y la rotación) en cada paquete de sincronización al servidor. El servidor luego envía esta información a otros jugadores, que establecen las velocidades recién recibidas en las entidades replicadas. Fue muy suave, sin diferencias notables entre los clientes.

Por supuesto, el problema aquí es que no hay un control centralizado sobre las entidades, pero creo que eso también podría hacerse en el lado del servidor haciendo una simulación del lado del servidor de la física.


Gracias por tu contribución. Requeriremos un control centralizado para evitar trampas, por lo que debemos hacer que el servidor ejecute una simulación al menos para saber si lo que los clientes dicen que están haciendo es posible o no.
Venesectrix

1

Personalmente, preferiría ejecutar las simulaciones solo en el servidor y hacer que transmita cualquier cambio en las velocidades / aceleraciones lineales / angulares de los objetos involucrados siempre que sucedan. Es, cuando un determinado objeto, por cualquier motivo, cambia cualquiera de sus propiedades físicas (como las velocidades y aceleraciones mencionadas anteriormente), este cambio específico se enviará del servidor al cliente, y el cliente cambiará su lado del datos del objeto en consecuencia.

La ventaja de esto sobre su implementación actual es que anulará la necesidad de interpolaciones del lado del cliente y generará un comportamiento muy fiel en los objetos. El problema es que este método es bastante vulnerable a las latencias, que se convierten en un gran problema cuando los jugadores están geográficamente demasiado lejos el uno del otro.

En cuanto a la pregunta 1, digo que el problema sería fluctuaciones en la latencia, porque no hay garantía absoluta de que haya un intervalo de 20 segundos exactamente perfecto entre cada recepción de la instantánea. Permítanme ilustrar (siendo "t" el tiempo medido en milisegundos):

1) En t = 20 desde el inicio del juego, el cliente recibió una instantánea e hizo la interpolación con éxito y sin problemas.

2) En t = 40, hubo una latencia entre el servidor y el cliente, y la instantánea solo llegó a t = 41.

3) En t = 60, el servidor envió otra instantánea, pero un segundo de la simulación se desperdició en el cliente debido a la latencia. Si la instantánea llega a t = 60, el cliente no hará una interpolación de los instantes 40 y 60, sino de los instantes 41 a 60, generando un comportamiento diferente. Esta inexactitud podría ser la causa de la eventual "sacudida".

En cuanto a la pregunta 2, su idea podría funcionar si implementa algo que rastree de manera eficiente si cada objeto está realmente sincronizado cliente-servidor sin tener que enviar paquetes a cada cuadro informando la posición de los objetos. Incluso si lo hace a intervalos discretos, no solo se ejecutará en el mismo problema de la pregunta 1, sino que también tendrá cantidades demasiado grandes de datos para transferir (lo cual es algo malo).


No estoy seguro de seguir lo que estás diciendo en tu primer párrafo. Si la simulación se ejecuta solo en el servidor y solo transmite cambios en la velocidad / aceleración, ¿cómo sabe el cliente dónde deben dibujarse los sprites? Los clientes tendrían que simular los objetos en función de la velocidad / aceleración recibida para dibujarlos correctamente. Creo que puede estar en lo cierto al recibir instantáneas a intervalos distintos de los que yo esperaba. ¿Alguna idea de cómo lidiar con eso?
Venesectrix

Los clientes conocen las posiciones iniciales y actuales, las velocidades y las aceleraciones de los objetos, y actualizarán la posición que cree que son los objetos (independientemente del servidor) en consecuencia. El servidor eventualmente cambiará tales propiedades en los clientes a través de mensajes, porque es el servidor el que está haciendo la detección de colisión y física (que
seguramente
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.