La versión avanzada de Little Bee actualmente está extrayendo la lógica de sincronización de cuadros en el SDK; consulte el proyecto https://github.com/dudu502/littlebee_libs. Este es un ejemplo de juego de sincronización de cuadros, que sincroniza cientos de objetos y miles de estados en el juego. El trasfondo del juego es un juego de disparos bajo un sistema planetario. El siguiente es el enlace del video.
[Ver reproduciendo el vídeo (Youtube)]
[Ver reproducción del vídeo (Youtube)]
[Mira reproducir el vídeo(bilibili)]
[Ver la reproducción del vídeo (bilibili)]
Sincronización de fotogramas | Sincronización de estados | |
---|---|---|
consistencia | El nivel de diseño determina la inevitable coherencia. | Puede garantizar la coherencia |
Número de jugadores | Soporte multijugador limitado | Varios jugadores tienen ventajas |
Multiplataforma | Necesidad de considerar la coherencia de las operaciones de punto flotante. | Dado que los cálculos principales se realizan en el servidor, no hay problemas multiplataforma. |
Anti-trampas | Fácil de engañar, pero se puede optimizar. | Puede ser muy bueno para prevenir las trampas. |
Desconectar y volver a conectar | Es difícil de implementar, pero no imposible. | Sólo necesitas reenviar el mensaje una vez, fácil de implementar |
Requisitos de reproducción | se puede realizar perfectamente | incapaz de lograr |
pausar el juego | Fácil de implementar | No es fácil de implementar |
Volumen de transmisión de red | relativamente pequeño | relativamente grande |
Dificultad de desarrollo | relativamente complejo | relativamente simple |
juegos de estrategia en tiempo real | Adecuado | No apto |
juegos de lucha | Adecuado | No apto |
juegos MOBA | Adecuado | No apto |
juegos MMO | No apto | Adecuado |
Después de comprender las dificultades que deben superarse en el proceso de desarrollo de la sincronización de cuadros, a continuación consideraremos elegir un mejor método de implementación o un marco de desarrollo. Dado que el desarrollo de la sincronización de cuadros requiere la separación de datos y rendimiento, ¿hasta qué punto debería separarse? La parte de cálculo de datos se puede incluso colocar en un hilo separado. La ventaja de escribir lógica de esta manera es que también permite que el servidor se ejecute para lograr la función de reproducir rápidamente el juego. Creo que solo ECS puede lograr este nivel de separación. La sincronización de cuadros más ECS es absolutamente un compañero perfecto.
Primero, debemos presentar ECS. ECS no es una tecnología nueva ni fue propuesta por primera vez por Unity. Este término apareció muy temprano y de repente se hizo popular en los últimos años debido a "Overwatch" de Blizzard. Los marcos de servidor y cliente de "Overwatch" están completamente construidos en base a ECS y tienen un rendimiento excelente en mecánica de juego, red y renderizado. Hablando francamente, ECS no es como un patrón de diseño. Todos los patrones de diseño que usamos antes se discutieron en diseño orientado a objetos, y ECS no está orientado a objetos. Unity también tiene ECS. De hecho, los propios componentes de Unity también son una especie de ECS, pero no son lo suficientemente puros. ECS es particularmente adecuado para el juego. Hay muchas variantes de ECS, y aquí está ECS con algunas ligeras modificaciones.
Esta es una característica de los juegos de sincronización de cuadros. Si un juego tiene un sistema de repetición, entonces el juego debe implementarse mediante sincronización de cuadros. La reproducción también se puede llamar grabación de video, pero es muy diferente de la reproducción con archivos de video, ya que el soporte generalmente ocupa un archivo enorme y la ventana no se puede cambiar durante el proceso de reproducción. Los videos son fácilmente robados, abusados y maliciosos. calidad modificada, comprimida y degradada, por lo que la reproducción de vídeo tiene una gran desventaja. La reproducción sincronizada con cuadros puede hacer que el archivo sea extremadamente pequeño y no pueda ser manipulado. Los usuarios pueden cambiar de ventana a voluntad durante el proceso de reproducción. Se puede decir que el sistema necesario para los juegos de sincronización de fotogramas es el sistema de repetición.
Aquí se recomienda RevenantX/LiteNetLib. Esta biblioteca es muy potente y fácil de usar. Proporciona una transmisión UDP confiable, que es exactamente lo que quiero. Hay muchos protocolos de datos para elegir para la comunicación de red. Estoy usando un protocolo de flujo binario hecho por mí mismo. Las funciones principales son la serialización y la deserialización. Como esta estructura de PtRoom:
//Template auto generator:[AutoGenPt] v1.0
//Creation time:2021/1/28 16:43:48
using System ;
using System . Collections ;
using System . Collections . Generic ;
namespace Net . Pt
{
public class PtRoom
{
public byte __tag__ { get ; private set ; }
public uint RoomId { get ; private set ; }
public byte Status { get ; private set ; }
public uint MapId { get ; private set ; }
public string RoomOwnerUserId { get ; private set ; }
public byte MaxPlayerCount { get ; private set ; }
public List < PtRoomPlayer > Players { get ; private set ; }
public PtRoom SetRoomId ( uint value ) { RoomId = value ; __tag__ |= 1 ; return this ; }
public PtRoom SetStatus ( byte value ) { Status = value ; __tag__ |= 2 ; return this ; }
public PtRoom SetMapId ( uint value ) { MapId = value ; __tag__ |= 4 ; return this ; }
public PtRoom SetRoomOwnerUserId ( string value ) { RoomOwnerUserId = value ; __tag__ |= 8 ; return this ; }
public PtRoom SetMaxPlayerCount ( byte value ) { MaxPlayerCount = value ; __tag__ |= 16 ; return this ; }
public PtRoom SetPlayers ( List < PtRoomPlayer > value ) { Players = value ; __tag__ |= 32 ; return this ; }
public bool HasRoomId ( ) { return ( __tag__ & 1 ) == 1 ; }
public bool HasStatus ( ) { return ( __tag__ & 2 ) == 2 ; }
public bool HasMapId ( ) { return ( __tag__ & 4 ) == 4 ; }
public bool HasRoomOwnerUserId ( ) { return ( __tag__ & 8 ) == 8 ; }
public bool HasMaxPlayerCount ( ) { return ( __tag__ & 16 ) == 16 ; }
public bool HasPlayers ( ) { return ( __tag__ & 32 ) == 32 ; }
public static byte [ ] Write ( PtRoom data )
{
using ( ByteBuffer buffer = new ByteBuffer ( ) )
{
buffer . WriteByte ( data . __tag__ ) ;
if ( data . HasRoomId ( ) ) buffer . WriteUInt32 ( data . RoomId ) ;
if ( data . HasStatus ( ) ) buffer . WriteByte ( data . Status ) ;
if ( data . HasMapId ( ) ) buffer . WriteUInt32 ( data . MapId ) ;
if ( data . HasRoomOwnerUserId ( ) ) buffer . WriteString ( data . RoomOwnerUserId ) ;
if ( data . HasMaxPlayerCount ( ) ) buffer . WriteByte ( data . MaxPlayerCount ) ;
if ( data . HasPlayers ( ) ) buffer . WriteCollection ( data . Players , ( element ) => PtRoomPlayer . Write ( element ) ) ;
return buffer . Getbuffer ( ) ;
}
}
public static PtRoom Read ( byte [ ] bytes )
{
using ( ByteBuffer buffer = new ByteBuffer ( bytes ) )
{
PtRoom data = new PtRoom ( ) ;
data . __tag__ = buffer . ReadByte ( ) ;
if ( data . HasRoomId ( ) ) data . RoomId = buffer . ReadUInt32 ( ) ;
if ( data . HasStatus ( ) ) data . Status = buffer . ReadByte ( ) ;
if ( data . HasMapId ( ) ) data . MapId = buffer . ReadUInt32 ( ) ;
if ( data . HasRoomOwnerUserId ( ) ) data . RoomOwnerUserId = buffer . ReadString ( ) ;
if ( data . HasMaxPlayerCount ( ) ) data . MaxPlayerCount = buffer . ReadByte ( ) ;
if ( data . HasPlayers ( ) ) data . Players = buffer . ReadCollection ( ( rBytes ) => PtRoomPlayer . Read ( rBytes ) ) ;
return data ;
}
}
}
}
Este es un proyecto de Unity basado en la sincronización de cuadros.
Algunas herramientas: herramienta de generación de estructura Pt, herramienta de generación Excel2Json, proyecto de biblioteca general, proyecto de biblioteca ServerDll
Documentos de diseño: documentos de diseño de esquema, documentos de diseño de prototipos, tablas de configuración.
Las tres figuras siguientes describen el uso del simulador de sincronización de cuadros en tres escenarios diferentes.
La siguiente figura muestra el comportamiento general del cliente y el servidor al mismo tiempo, y la lógica de reproducción también corresponde al mismo comportamiento.
Esta imagen muestra al cliente y al servidor ejecutando la lógica en cada TICK lógico. La parte superior es el cliente. La lógica que el cliente necesita ejecutar incluye la parte ECSR y la parte inferior es la parte del servidor.
La última imagen describe cada cuadro lógico de reproducción.
A través de estas imágenes y los tipos específicos de juegos, podemos configurar un Sistema y un Componente personalizados para manejar la lógica relacionada.
Este es un proyecto de recopilación de servicios, que incluye WebServer, GateServer, RoomServer, etc.