Estoy interesado en evaluar las diferentes formas en que el netcode puede "engancharse" a un motor de juego. Estoy diseñando un juego multijugador ahora, y hasta ahora he determinado que necesito (al menos) tener un hilo separado para manejar los zócalos de red, distinto del resto del motor que maneja el bucle de gráficos y las secuencias de comandos.
Tenía una manera potencial de hacer que un juego en red sea de un solo subproceso, que es verificar la red después de renderizar cada cuadro, utilizando sockets sin bloqueo. Sin embargo, esto claramente no es óptimo porque el tiempo que lleva renderizar un cuadro se agrega al retraso de la red: los mensajes que llegan a través de la red deben esperar hasta que se complete el procesamiento actual del cuadro (y la lógica del juego). Pero, al menos de esta manera, la jugabilidad seguiría siendo fluida, más o menos.
Tener un hilo separado para la conexión en red permite que el juego responda completamente a la red, por ejemplo, puede enviar un paquete ACK instantáneamente al recibir una actualización de estado del servidor. Pero estoy un poco confundido acerca de la mejor manera de comunicarse entre el código del juego y el código de red. El hilo de red empujará el paquete recibido a una cola, y el hilo del juego leerá de la cola en el momento apropiado durante su ciclo, por lo que no nos hemos librado de este retraso de hasta un cuadro.
Además, parece que me gustaría que el subproceso que maneja el envío de paquetes esté separado del que está buscando paquetes que salen por la tubería, porque no podría enviar uno mientras está en el medio de comprobando si hay mensajes entrantes. Estoy pensando en la funcionalidad de select
o similar.
Supongo que mi pregunta es, ¿cuál es la mejor manera de diseñar el juego para la mejor capacidad de respuesta de la red? Claramente, el cliente debe enviar la entrada del usuario lo antes posible al servidor, para que pueda recibir el código de envío de red inmediatamente después del ciclo de procesamiento de eventos, ambos dentro del ciclo del juego. ¿Tiene esto algún sentido?
fuente
No, no lo haces.
No necesariamente importa. ¿Cuándo se actualiza tu lógica? No tiene sentido sacar datos de la red si aún no puede hacer nada con ella. Del mismo modo, no tiene mucho sentido responder si aún no tiene nada que decir.
Si su juego es tan rápido que esperar el siguiente cuadro será un retraso significativo, entonces enviará suficientes datos para que no necesite enviar paquetes ACK separados, solo incluya valores ACK dentro de sus datos normales cargas útiles, si es que las necesita.
Para la mayoría de los juegos en red, es perfectamente posible tener un bucle de juego como este:
Puede desacoplar las actualizaciones del renderizado, lo cual es muy recomendable, pero todo lo demás puede mantenerse así de simple a menos que tenga una necesidad específica. ¿Qué tipo de juego estás haciendo exactamente?
fuente
Eso no es cierto en absoluto. El mensaje pasa por la red mientras el receptor representa la trama actual. El retraso de la red está sujeto a un número entero de tramas en el lado del cliente; sí, pero si el cliente tiene tan pocos FPS que esto es un gran problema, entonces tienen mayores problemas.
fuente
Las comunicaciones de red deben estar agrupadas. Debes esforzarte por obtener un solo paquete enviado cada tick del juego (que a menudo es cuando se procesa un marco, pero realmente debería ser independiente).
Sus entidades de juego hablan con el subsistema de red (NSS). El NSS agrupa mensajes, ACK, etc. y envía algunos (con suerte, uno) paquetes UDP de tamaño óptimo (por lo general, ~ 1500 bytes). El NSS emula paquetes, canales, prioridades, reenvío, etc., mientras que solo envía paquetes UDP únicos.
Lea el tutorial del gaffer en los juegos o simplemente use ENet que implementa muchas de las ideas de Glenn Fiedler.
O simplemente puede usar TCP si su juego no necesita reacciones de contracción. Luego, todos los problemas de lotes, reenvíos y ACK desaparecen. Sin embargo, aún querría un NSS para administrar el ancho de banda y los canales.
fuente
No ignores completamente la capacidad de respuesta. Hay poco que ganar al agregar otra latencia de 40 ms a los paquetes ya retrasados. Si está agregando un par de cuadros (a 60 fps), está retrasando el procesamiento de una actualización de posición de otro par de cuadros. Es mejor aceptar paquetes rápidamente y procesarlos rápidamente para mejorar la precisión de la simulación.
He tenido un gran éxito al optimizar el ancho de banda al pensar en la información de estado mínima necesaria para representar lo que es visible en la pantalla. Luego observa cada bit de datos y elige un modelo para ello. La información de posición se puede expresar como valores delta a lo largo del tiempo. Puede usar sus propios modelos estadísticos para esto y pasar años depurándolos, o puede usar una biblioteca para ayudarlo. Prefiero usar el modelo de punto flotante de esta biblioteca DataBlock_Predict_Float. Esto hace que sea realmente fácil optimizar el ancho de banda utilizado para el gráfico de la escena del juego.
fuente