Uma pequena abelha versão avançada está atualmente extraindo a lógica de sincronização de quadros para o SDK, consulte o projeto https://github.com/dudu502/littlebee_libs Este é um exemplo de jogo de sincronização de quadros, sincronizando centenas de objetos e milhares de estados no jogo O pano de fundo do jogo é um jogo de tiro em um sistema planetário. A seguir está o link do vídeo.
[Assista ao vídeo (Youtube)]
[Assista ao replay do vídeo (Youtube)]
[Assista ao vídeo (bilibili)]
[Assista a repetição do vídeo (bilibili)]
Sincronização de quadros | Sincronização de estado | |
---|---|---|
consistência | O nível de design determina a consistência inevitável | Pode garantir consistência |
Número de jogadores | Suporte multijogador limitado | Vários jogadores têm vantagens |
Plataforma cruzada | Precisa considerar a consistência das operações de ponto flutuante | Como os cálculos principais são feitos no servidor, não há problemas de plataforma cruzada. |
Anti-trapaça | Fácil de trapacear, mas pode ser otimizado | Pode ser muito bom na prevenção de trapaças |
Desconectar e reconectar | É difícil de implementar, mas não é impossível | Você só precisa reenviar a mensagem uma vez, fácil de implementar |
Requisitos de reprodução | pode ser realizado perfeitamente | Incapaz de alcançar |
pausar jogo | Fácil de implementar | Não é fácil de implementar |
Volume de transmissão de rede | relativamente pequeno | relativamente grande |
Dificuldade de desenvolvimento | relativamente complexo | relativamente simples |
Jogos RTS | Adequado | Não adequado |
jogos de luta | Adequado | Não adequado |
Jogos MOBA | Adequado | Não adequado |
Jogos MMO | Não adequado | Adequado |
Depois de compreender as dificuldades que precisam ser superadas no processo de desenvolvimento da sincronização de quadros, consideraremos a seguir a escolha de um melhor método de implementação ou de uma estrutura de desenvolvimento. Como o desenvolvimento da sincronização de quadros requer a separação entre dados e desempenho, até que ponto eles deveriam ser separados? A parte de cálculo de dados pode até ser colocada em um thread separado. A vantagem de escrever a lógica dessa forma é que ela também permite que o servidor execute a função de repetir o jogo rapidamente. Acho que apenas o ECS pode atingir esse nível de separação. A sincronização de quadros mais ECS é um parceiro absolutamente perfeito.
Primeiro, precisamos apresentar o ECS não é uma tecnologia totalmente nova, nem foi proposta pela Unity. Este termo apareceu muito cedo e de repente se tornou popular nos últimos anos por causa do "Overwatch" da Blizzard. As estruturas de servidor e cliente de "Overwatch" são totalmente construídas com base em ECS e possuem excelente desempenho em mecânica de jogo, rede e renderização. Falando francamente, o ECS não é como um padrão de design. Os padrões de design que usamos antes foram todos discutidos no design orientado a objetos, e o ECS não é orientado a objetos. O Unity também possui ECS. Na verdade, os próprios componentes do Unity também são uma espécie de ECS, mas não são puros o suficiente. ECS é particularmente adequado para jogabilidade. Existem muitas variantes do ECS, e aqui está o ECS com algumas pequenas modificações.
Este é um recurso dos jogos de sincronização de quadros. Se um jogo tiver um sistema de repetição, o jogo deverá ser implementado por meio da sincronização de quadros. A reprodução também pode ser chamada de gravação de vídeo, mas é muito diferente da gravação de vídeo usando arquivos de vídeo, pois a mídia geralmente ocupa um arquivo enorme e a janela não pode ser trocada durante o processo de reprodução. Os vídeos são facilmente roubados, abusados e mal-intencionados. qualidade modificada, compactada e degradada, portanto a reprodução de vídeo tem uma grande desvantagem. A reprodução sincronizada com quadros pode tornar o arquivo extremamente pequeno e não pode ser adulterado. Os usuários podem alternar as janelas à vontade durante o processo de reprodução. Pode-se dizer que o sistema necessário para jogos com sincronização de frames é o sistema de replay.
RevenantX/LiteNetLib é recomendado aqui. Esta biblioteca é muito poderosa e simples de usar. Ela fornece transmissão UDP confiável, que é exatamente o que eu quero. Existem muitos protocolos de dados para escolher para comunicação de rede. Estou usando um protocolo de fluxo binário criado por mim. As principais funções são serialização e desserialização. Como esta estrutura 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 é um projeto Unity baseado em sincronização de quadros
Algumas ferramentas: ferramenta de geração de estrutura Pt, ferramenta de geração Excel2Json, projeto de biblioteca geral, projeto de biblioteca ServerDll
Documentos de projeto: documentos de projeto de esboço, documentos de projeto de protótipo, tabelas de configuração.
As três figuras a seguir descrevem o uso do simulador de sincronização de quadros em três cenários diferentes.
A figura abaixo mostra o comportamento geral do cliente e do servidor ao mesmo tempo, e a lógica de reprodução também corresponde ao mesmo comportamento.
Esta imagem mostra a lógica de execução do cliente e do servidor em cada TICK lógico. A parte superior é o cliente. A lógica que o cliente precisa executar inclui a parte ECSR e a parte inferior é a parte do servidor.
A última imagem descreve cada quadro lógico de reprodução.
Através dessas imagens e dos tipos específicos de jogos, podemos configurar sistemas e componentes personalizados para lidar com a lógica relacionada.
Este é um projeto de coleta de serviços, incluindo WebServer, GateServer, RoomServer, etc.