Como sincronizar relógios no jogo multiplayer?

8

Eu tenho 2 a 3 clientes, que podem trocar mensagens através do Apple Game Center.

A única sincronização que eu preciso é: inicie o jogo no mesmo momento.

Eu acho que isso envolve sincronização de relógio. Como conseguir isso?

GameCoder
fonte
3
A resposta curta é 'você não pode'. O inevitável (e inconsistente) atraso nas comunicações entre dispositivos significa que, mesmo se um dispositivo disser 'Acredito que agora são 12: 00: 00.00', poderá facilmente estar entre 12: 00: 00.10 e 12:00:05 pelo vez que a mensagem for recebida em outro lugar. Em um modelo baseado em servidor, você não pode fazer muito melhor do que apenas mandar o servidor enviar a mensagem 'Iniciar jogo' para os clientes ao mesmo tempo.
9608 Steven Stadnicki
Bom que eu não preciso de perfeição. Como fazê-lo melhor do que nada? Talvez: cada jogador envia a mensagem "INICIAR" e começa assim que recebe a mensagem INICIAR de todos os outros jogadores
GameCoder
11
@StevenStadnicki Atrasos na rede não tornam isso impossível. O problema geral é chamado de "sincronização do relógio" e é bem estudado. (No entanto, se você só controlar os pontos finais, então não é possível conta para assimetria de atrasos.)
Praxeolitic
Talvez eu esteja entendendo mal o seu problema, mas você não pode apenas fazer com que a mensagem que você envia aos clientes indique um horário de início um pouco no futuro? Se for a hora nowe você enviar uma mensagem para começar exatamente now + halfSecondentão, desde que todos recebam a mensagem em meio segundo e, enquanto os relógios do sistema estiverem sincronizados corretamente, todos serão iniciados ao mesmo tempo.
Mark

Respostas:

12

O comentário de Steven está certo: isso é teoricamente impossível de fazer.

Felizmente, na prática, você pode se aproximar, e é assim que coisas como o NTP funcionam.

Por exemplo, melhor do que apenas enviar uma mensagem para três clientes dizendo "iniciar agora", você pode trocar algumas mensagens de ping com antecedência para medir o tempo que leva para receber uma mensagem para o cliente e, quando você envia a mensagem inicial, em vez de "iniciar agora", diga "iniciar em X milissegundos" e ajuste X para os diferentes momentos necessários para que uma mensagem chegue.

por exemplo.:

  • Você envia uma mensagem para o cliente 1 e recebe uma resposta 20 ms depois. Você acha que são necessários aproximadamente 10 ms para obter uma mensagem para o cliente 1.
  • Você faz o mesmo para o Cliente 2 e obtém uma resposta 28ms depois - portanto, o tempo de transmissão provavelmente será de 14ms.
  • Então você envia uma mensagem para o Cliente 1 dizendo "inicie o jogo em 50ms" e envie uma para o Cliente 2 dizendo "inicie o jogo em 46ms". O Cliente 2 receberá a mensagem cerca de 4 ms mais tarde, mas aguardará 4 ms menos antes de iniciar o jogo.

Isso não pode garantir a sincronização porque o tempo gasto para enviar uma mensagem pela Internet varia e porque pode ser diferente em cada direção. Na primeira, você pode reduzir os efeitos realizando a medição várias vezes e fazendo uma leitura mediana. O segundo é mais complicado e pode ser teoricamente impossível de resolver (embora não me lembre da prova no momento). A boa notícia é que você provavelmente não precisa de tanta precisão.

Kylotan
fonte
Provavelmente precisarei limitar a atividade de meus aplicativos durante a sincronização - agora, dedico algum tempo de CPU ao subsistema de rede em cada quadro de desenho. Se a taxa de quadros for de 30 FPS, responderei o ping em 1/30 = 33 ms de qualquer maneira. Eu também poderia adicionar os 33 ms, ou a diferença entre "pilha TCP receber time" e os meus MilisecondTicks atuais () ..
GameCoder
2
Se você deseja manter os sistemas sincronizados com precisão quadro a quadro durante o jogo, acho que é um jogo perdido e você deve desistir agora. Relógios e latência flutuam e as informações sempre levam tempo para viajar. Tudo o que você está tentando fazer, provavelmente não precisa desse nível de sincronização.
Kylotan
Acho que estamos perdendo o objetivo. Não é a perfeição que me incomoda, é a experiência do usuário. Isso pode ser bom ou melhor, e eu gostaria que fosse melhor. É isso aí.
GameCoder 12/12/12
2
Uma boa experiência do usuário não requer sincronização precisa. É por isso que a maioria dos jogos não tenta.
Kylotan
11
Teoricamente, é impossível se cada jogador controla seu próprio bastão, porque leva um tempo não zero para que essas informações cheguem ao outro computador, durante o qual os dois lados têm informações diferentes. Em vez disso, você encontra maneiras de lidar com as diferenças. Seu problema não é realmente diferente de qualquer outro jogo on-line em ritmo acelerado e há várias outras perguntas sobre como lidar com a rede para eles neste site. Um deles está aqui: gamedev.stackexchange.com/questions/22444/…
#
3

Como mencionado, isso é impossível, então eu tentaria outra abordagem:

Se você não tiver um servidor dedicado, escolha um cliente participante para se tornar o host (isso pode ser transferido se necessário).

O host agora executará toda a lógica importante do jogo, como detecção de hits, controles de IA, manipulação de inventário etc., bem como rastreamento de tempo (por exemplo, ditar o tempo do jogo).

Os outros clientes apenas tentarão permanecer sincronizados com o host, tentando estimar ou aproximar o valor esperado. Se o atraso aumentar ou houver perda de pacotes, as coisas podem ficar instáveis, mas é trivial recuperar o atraso, basicamente apenas aguardando a próxima atualização.

A maioria dos jogos (especialmente o FPS) oculta esse fato fazendo seu próprio cálculo local para o próprio movimento do jogador, tiros sendo disparados etc. para evitar que o jogo fique lento. Tudo ainda é corrigido com base nos dados do servidor. Isso pode levar a alguma confusão, por exemplo, você se vê atirando no inimigo, mas no mesmo momento em que cai morto (sem que o inimigo seja atingido), mas ainda é uma abordagem muito melhor do que a sincronização completa.


Se você ainda insistir em manter tudo em sincronia, provavelmente desejará criar algum tipo de etapa ou contador de quadros, para que todos os clientes processem apenas uma etapa lógica e depois sincronizem seus dados, etc. Lembre-se de que essa pode ser a largura de banda intensivo e lento, então eu não recomendaria fazer isso, a menos que você tenha muitos dados e sua jogabilidade seja baseada em turnos (por exemplo, jogos no estilo de artilharia / worms).

Mario
fonte
1

Eu recomendo sincronizar os cronômetros do sistema em todos os clientes e no servidor por meio do protocolo NTP [Stratum 2]; o servidor envia um comando para iniciar o jogo no horário especificado, por exemplo, quando todos os cronômetros atingem 0:05:00. Essa abordagem deve fornecer uma sincronização precisa de 3-4 ms, acredito.

ivan866
fonte