Sincronização de movimento baseada em blocos multijogador

8

Tenho que sincronizar o movimento de vários jogadores pela Internet e estou tentando descobrir a maneira mais segura de fazer isso.

O jogo é baseado em blocos, você só pode se mover em 4 direções, e cada movimento move o sprite 32px (com o tempo, é claro). Agora, se eu simplesmente enviasse essa ação de movimentação para o servidor, a qual seria transmitida a todos os jogadores, enquanto a tecla Walk estiver pressionada, para continuar andando, eu tenho que executar o próximo comando, enviá-lo ao servidor, e para todos os clientes, com o tempo, ou o movimento não será mais tranquilo. Vi isso em outros jogos, e pode ficar feio bem rápido, mesmo sem atrasos. Então, eu estou querendo saber se esta é mesmo uma opção viável. Esse parece ser um método muito bom para um jogador, já que é fácil e direto (basta executar a próxima ação de movimento a tempo e adicioná-la a uma lista), e você pode adicionar facilmente o movimento do mouse (clicando em algum bloco), para adicionar um caminho a uma fila que é percorrida.

A outra coisa que me veio à mente foi enviar as informações de que alguém começou a se mover em alguma direção e, novamente, uma vez que ele parou ou mudou de direção, junto com a posição, para que o sprite apareça na posição correta, ou melhor, para que a posição pode ser corrigida se estiver errada. Isso deve (espero) causar problemas apenas se alguém realmente estiver atrasado; nesse caso, é de se esperar. Para que isso funcione, eu precisaria de algum tipo de fila, onde as mudanças de direção recebidas e outras coisas são salvas, para que o sprite saiba para onde ir, após a conclusão do movimento atual para o próximo bloco. Isso pode realmente funcionar, mas parece meio complicado demais. Embora possa ser a única maneira de fazer isso, sem risco de gagueira. Se uma parada ou mudança de direção é recebida no lado do cliente, é ' s salvos em uma fila e o char continua se movendo para as coordenadas especificadas, antes de parar ou mudar de direção. Se o novo comando chegar tarde demais, também haverá gagueira ...

Estou com dificuldades para decidir sobre um método e ainda não consegui encontrar nenhum exemplo disso. Meu principal problema é manter o movimento do bloco suave, e é por isso que outros tópicos relacionados à sincronização do movimento baseado em pixel não estão ajudando muito.

Qual é a maneira "padrão" de fazer isso?

Marte
fonte

Respostas:

4

Suponho que você esteja falando sobre mudar em tempo real. Tentar interpolar a posição provavelmente é uma causa perdida; em uma conexão lenta, o estado do jogo pode ficar cada vez mais atrás do estado atual do jogo. Ao contrário do outro comentário, eu recomendaria não colocar muita lógica de jogo no lado do servidor. Quando eu implementei esse tipo de solução, o cliente tem um soquete TCP para o servidor constantemente. Quando o estado do jogo muda, ele envia uma mensagem para o servidor, como 'client is at x = 10, y = 20'. O servidor envia a mensagem aos outros clientes, que tentam lidar com isso com base em sua própria lógica interna. O uso do TCP garante que suas mensagens cheguem em ordem.

Infelizmente, mesmo jogos AAA não conseguem lidar com atrasos extremos; ocasionalmente, os jogadores se movem muito rapidamente, porque seus estados local e de rede estão muito longe do normal. Monitorar o ping de ida e volta e chutar os usuários que estão muito longe / lentos ajudará com isso.

Alan Gardner
fonte
11
+1 para 'mesmo jogos AAA não suportam lag extremo'; Na verdade, ninguém consegue lidar perfeitamente com pequenos atrasos. Podemos fazer com que pareça que podemos.
Valmond
3

Basicamente, é assim que você pode fazer de uma maneira simples e segura (aviso, pseudocódigo extremo):

A) O cliente (playerid = 5) pede que o servidor se mova para a direita (ou melhor, para ladrilhar 73,18)

B) O servidor verifica seu grande mapa 2D que contém jogadores (ou ID de jogadores, por exemplo):

 if(map[73][18] == occupied)
     Send back ('Not OK') to client (the case is blocked by another player)
else
    Set the new position as blocked: map[73][18]=5
    Release the old position: map[72][18]=0;
    Send a 'new position: 5, 73,18' message to all players who are nearby (moving client included)

C) Os clientes recebem uma mensagem de 'nova posição' (bem, ou o jogador 5 recebe a mensagem 'Não aprovado')

D) os clientes movem lentamente o item que possui playerid = 5 para sua nova posição (ou o cria se não tiver um item com playerid = 5)

Valmond
fonte
Bem, obrigado, mas o meu problema é mantê-lo liso sobre os outros clientes ao longo de vários continua movimentos telha, não o movimento azulejo em geral ^^
Mars
Defina a velocidade corretamente e compense o atraso com, por exemplo, acerto de contas morto ou apenas um movimento 'estúpido' em direção ao alvo em velocidade constante '(ou velocidade proporcional sobre um determinado tesouro, se você enviar timestamps ao longo das posições) e você estará bem.
Valmond
1

Eu acho que o método que você descreveu é muito bom, especialmente em um jogo simplista onde apenas 4 movimentos são permitidos. Aqui está como eu o implementei.

Não conheço nenhum padrão, mas a primeira coisa que me veio à mente foi transmitir apenas "alterações de entrada", alterações na entrada de um jogador, de um jogador para os outros. Por exemplo, você pode transmitir apenas mensagens ao longo das linhas de [tecla pressionada, ID da tecla, hora] ou [tecla liberada, ID da tecla, hora] e deixar o jogo dos jogadores agir sobre essas alterações de entrada. Incluir o tempo ajudará na correção do caminho (se o jogador mudou de direção no tempo t, será necessário corrigir o "avançar" no tempo t + 1).

No entanto, com esta solução, você pode encontrar o problema de mensagens perdidas; suponha que a mensagem "chave liberada" nunca foi enviada? Algo que pode ajudar isso é pesquisar periodicamente o estado do jogo de um jogador e retornar a posição atual do jogador, assim como qualquer tecla pressionada, ou até retornar o estado inteiro do jogo. Dependendo do número de jogadores e comandos, você pode ajustar isso para equilibrar a passagem de mensagens entre clientes / servidor para obter um bom equilíbrio.

Outra coisa que você pode analisar é o planejamento de caminhos. Por exemplo, se o jogador estiver avançando por 5s, no próximo milissegundo as chances são de que o jogador continuará avançando. Nesse caso, você pode criar uma aparência de previsão de caminho para evitar atrasos. Isso é essencialmente o que o método acima está fazendo, embora isso possa levar em consideração caminhos circulares.

Espero que isso tenha ajudado!

Jonathan Pitre
fonte
11
O problema de apenas enviar entradas é que, se você perder uma única ou, às vezes, apenas interpretá-las na ordem errada, seu estado de jogo não ficará mais bem, ou seja. dois clientes mostrarão diferentes 'telas'.
Valmond
@ Valmond ponto muito bom ... na verdade, eu sinto vontade de excluir minha resposta, uma vez que sob essa luz parece incorreto.
Jonathan Pitre
11
Você pode enviar as entradas para o servidor (até que esteja tudo bem), mas o servidor deve (IMO) enviar dados exatos ou pelo menos verificar os dados ou talvez enviar todo o estado do jogo (isso foi feito em jogos reais, por exemplo, Blood Bowl ), talvez você pode ajustar sua resposta, em vez de excluí-lo :-)
Valmond