Com que frequência atualizar um cliente de jogo sobre o mundo?


10

Usando o socket.io , tenho uma comunicação semelhante à de outros MMORPGs, uma conexão constante com as mensagens.

No meu design até agora, o cliente envia a posição do jogador e o quadro de animação a cada quadro de atualização. Quando o servidor recebe essa mensagem, ela a transmite a todos os clientes, que então moverão o gráfico de acordo.

Seria uma idéia melhor coletá-las e transmiti-las, digamos, uma vez em 1/10 de segundo?

Além disso, o cliente deve enviar muitas mensagens diferentes (exp ganho, clicadas no item) assim que ocorrerem ou apenas uma coletada? O primeiro seria implementado mais facilmente.

Respostas:


16

Vou abordar isso a partir de uma discussão de alto nível e depois trabalhar para suas perguntas. Para fins de divulgação, não tenho experiência pessoal com o uso do socket.io, mas muita exposição ao espaço problemático em relação aos MMORPGs.

O design da arquitetura de rede de um mecanismo dos MMORPGs e / ou a seleção de um projeto de software intermediário ou de código aberto para fornecer a funcionalidade é uma das decisões mais desafiadoras influenciadas pelo design do jogo, orçamento e conhecimento técnico da equipe. A escolha final influenciará outras decisões arquiteturais (e às vezes também as decisões de design).

Como desenvolvedores de MMORPGs, planejamos um grande sucesso (também conhecido como sucesso catastrófico), onde grandes números acionam luzes de aviso e sirenes. Um dos grandes números assustadores que surgem é em algoritmos N-quadrado (N ^ 2 adiante), em sua pergunta, a primeira coisa que me ocorreu foi que parecia que o design exigia que uma entidade transmitisse informações para todas as outras entidades conectadas. Este é o exemplo clássico de um problema N ^ 2.

Os MMOs geralmente abordam os problemas de N ^ 2 atacando o problema de várias maneiras diferentes; sistemas de conscientização (situacional, espacial, etc.) em que uma entidade está ciente de algum subconjunto de todas as outras entidades, particionando jogadores em diferentes "fragmentos", particionando jogadores em "zonas" e / ou instâncias, implementando mecânica de jogo que desencoraja muitos jogadores de se reunir (tempestades de teletransporte de Asheron Call), etc.

A maioria dos MMORPGs e muitos mecanismos de FPS possuem arquiteturas de rede bastante sofisticadas que suportam uma variedade de recursos, incluindo:

  • vias de comunicação confiáveis ​​e não confiáveis ​​(TCP, implementações personalizadas de pacotes UDP e UDP confiáveis)
  • modelagem da largura de banda (priorização, duração, etc.)
  • replicação automática de dados de campo / variáveis ​​e chamadas de função
  • conjuntos atômicos de dados (ou seja, dados que são comunicados juntos)
  • atualizações discretas (ou seja, onde toda transição é importante)
  • correção de latência
  • uma variedade de truques para fazer com que o cliente se sinta responsivo

Acho que a documentação irreal de rede e a documentação de rede de válvulas fornecem uma boa cartilha sobre uma variedade de problemas.

Então, agora vamos abordar as perguntas.

Seria uma idéia melhor coletá-las e transmiti-las, digamos, uma vez em 1/10 de segundo?

É difícil fornecer uma resposta simples sim ou não aqui ... porque depende da escala (número de entidades observadoras), frequência das atualizações e tamanho das atualizações. Por exemplo, coletar todas elas pode estar terrivelmente errado se o tamanho das atualizações puder explodir um buffer em algum lugar.

O cliente para jogos MMORPGs e FPS geralmente é projetado de modo que eles visualizem algo que "parece" certo, mesmo que não recebam uma atualização por muito mais quadros de atualização do que o "normal". Ao usar a comunicação não confiável (UDP), você pode esperar perder algumas atualizações no vazio, os clientes podem compensar isso enviando atualizações mais frequentes do que as usadas em um transporte confiável.

De uma revisão superficial da documentação do socket.io, parece que ele suporta caminhos de comunicação confiáveis ​​e não confiáveis ​​(voláteis na sua terminologia).

Eu abordaria isso primeiro abordando, com que frequência as atualizações são necessárias ...

Se um jogador está se movendo em linha reta a uma taxa constante, uma frequência de atualização mais baixa é boa porque os clientes observadores podem prever com alta precisão onde o jogador estará a qualquer momento. Quando um jogador está girando em um círculo fechado ou fazendo mudanças rápidas de direção, são necessárias atualizações muito mais frequentes. Por outro lado, quando um jogador não está se movendo, não há motivo para enviar atualizações de movimento.

Não importa o que seja, provavelmente não é (geralmente) necessário enviar atualizações a cada quadro do cliente para o servidor. O próprio servidor pode optar por enviar mensagens para cada quadro onde as possui ou atrasá-las (consulte a configuração da largura de banda, a priorização e a vida útil da atualização).

Outros tipos de atualizações têm características diferentes ... por exemplo, considere um campo de "saúde" que é modificado quando um jogador ou criatura é danificado. Uma maneira de implementar isso é transmitir cada alteração imediatamente quando isso acontece, mas isso leva ao desperdício de processamento e largura de banda se o valor for alterado várias vezes em um quadro ou em quadros consecutivos (arquiteturas de rede que implementam o formato da largura de banda resolvem esse problema, combinando as atualizações em somente o mais recente é enviado a um cliente observador quando ele tem largura de banda disponível).

o cliente deve enviar muitas mensagens diferentes (exp ganho, clicadas no item) assim que elas ocorrerem ou apenas uma coletada?

Novamente, nenhuma resposta simples de sim ou não funcionará aqui. Dependendo do que exatamente você quer dizer com coletado ... ambos podem estar certos em diferentes circunstâncias e também dependem da implementação da camada de rede.

A coleta de mensagens para uma entidade específica a ser enviada como uma mensagem pode (dependendo da implementação) reduzir a sobrecarga de largura de banda para o envio de uma mensagem (reduzindo seus custos) de maneira inversa (dependendo da implementação, como mapeamentos de campo / valor comunicados por seqüências de caracteres) pode aumentar a largura de banda requisitos em comparação com um tipo de mensagem específico mais simples.

Revendo a documentação do socket.io, parece-me que a sobrecarga de mensagens está na extremidade superior do espectro, o que favoreceria a coleta de atualizações para enviar como uma mensagem agregada, em oposição a muitas atualizações únicas.

Eu recomendo revisar todas as atualizações que você pensa em replicar, por exemplo, a maioria dos MMORPGs e FPSs não se preocupam em enviar o jogador clicado em X eventos para observar clientes, a menos que isso resulte em uma mudança de estado para um objeto do qual eles também estavam cientes .


3
+1 ótima resposta. Além disso, eu sugeriria começar com simplicidade e otimizar a partir daí. As mensagens de spam, claro, vão à loucura e a largura de banda começa a diminuir. É por isso que é bom ter realizado testes de estresse. Você implementa a abordagem mais ingênua possível e faz alguns testes; você faz algumas otimizações; você faz o teste de estresse novamente, otimiza ainda mais, enxágua e repete. Se for absolutamente trivial implementar uma otimização no primeiro dia, vá em frente; mas esteja ciente de que mesmo as otimizações mais triviais podem conter suposições que podem ser refutadas posteriormente em um ambiente de produção.
Engenheiro de

5

Aqui está um exemplo do mundo real: o RuneScape "marca" uma vez a cada ~ 0,6 segundos. Você pode ler sobre isso aqui . Eu imagino que isso simplifica as coisas da perspectiva deles, pois em seus scripts eles provavelmente especificam tempos e atrasos nos ticks em vez de milissegundos. E, é claro, com uma taxa de tick tão grande / lenta, os usuários com conexões lentas não estão em grande desvantagem em comparação com outros jogadores.


0

Uma maneira de trabalhar é dissociar as alterações reais dos dados da transmissão dessas alterações. Quando um objeto for alterado, faça a modificação e defina um sinalizador 'sujo' e, quando chegar a hora de transmitir quaisquer alterações, envie apenas dados para objetos sinalizados e limpe o sinalizador. Os valores que são alterados várias vezes são mesclados automaticamente aqui, porque sua atualização envia apenas o estado no momento da transmissão. Você também pode sinalizar subobjetos ou propriedades individuais para transmitir apenas o que mudou.

Ah, e geralmente você não enviaria os quadros de animação para o servidor ou para os outros clientes. O estado preciso da animação geralmente é considerado um detalhe da apresentação e é deixado para cada cliente resolver, e você deve garantir que cada cliente saiba qual animação está sendo reproduzida no momento.


Eu não? E quanto a poses ou algo assim, preciso ter certeza de que todos os clientes veem o mesmo?
Lanbo

É bastante impraticável garantir que todos os clientes vejam exatamente a mesma coisa, porque as informações levam tempo para viajar, diferentes clientes têm latência de rede diferente, são renderizados em velocidades diferentes etc. Então, tentar fazer com que todos mostrem exatamente o mesmo quadro simultaneamente é geralmente esforço fútil. Em vez disso, tente garantir que eles iniciem a mesma animação aproximadamente ao mesmo tempo, e isso é bom o suficiente.
Kylotan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.