Estou interessado em avaliar as diferentes maneiras pelas quais o netcode pode "se conectar" a um mecanismo de jogo. Estou projetando um jogo multiplayer agora e até agora concluí que preciso (pelo menos) de um thread separado para lidar com os soquetes de rede, distinto do restante do mecanismo que lida com o loop e o script gráficos.
Eu tinha uma maneira potencial de criar um jogo em rede inteiramente de thread único, ou seja, verificar a rede depois de renderizar cada quadro, usando soquetes sem bloqueio. No entanto, isso claramente não é ideal porque o tempo necessário para renderizar um quadro é adicionado ao atraso na rede: As mensagens que chegam pela rede devem esperar até que a renderização do quadro atual (e a lógica do jogo) seja concluída. Mas, pelo menos assim, a jogabilidade ainda permaneceria suave, mais ou menos.
Ter um encadeamento separado para rede permite que o jogo seja totalmente responsivo à rede, por exemplo, pode enviar de volta um pacote ACK instantaneamente após o recebimento de uma atualização de estado do servidor. Mas estou um pouco confuso sobre a melhor maneira de me comunicar entre o código do jogo e o código da rede. O encadeamento de rede empurrará o pacote recebido para uma fila, e o encadeamento do jogo será lido na fila no momento apropriado durante o loop, portanto, não nos livramos desse atraso de um quadro.
Além disso, parece que eu gostaria que o encadeamento que lida com o envio de pacotes fosse separado daquele que está verificando pacotes descendo pelo canal, porque não seria capaz de enviar um enquanto estiver no meio de verificando se há mensagens recebidas. Estou pensando na funcionalidade select
ou similar.
Acho que minha pergunta é: qual é a melhor maneira de projetar o jogo para obter a melhor capacidade de resposta da rede? Claramente, o cliente deve enviar a entrada do usuário o mais rápido possível para o servidor, para que eu possa ter o código de envio de rede imediatamente após o loop de processamento do evento, ambos dentro do loop do jogo. Isto faz algum sentido?
fonte
Não, você não.
Não importa necessariamente. Quando sua lógica é atualizada? Não adianta tirar os dados da rede se você ainda não pode fazer nada. Da mesma forma, não adianta responder se você ainda não tem nada a dizer.
Se o seu jogo é tão rápido que esperar o renderização do próximo quadro for um atraso significativo, enviará dados suficientes para que você não precise enviar pacotes ACK separados - inclua valores ACK nos dados normais cargas úteis, se você precisar delas.
Para a maioria dos jogos em rede, é perfeitamente possível ter um loop de jogo como este:
Você pode separar as atualizações da renderização, o que é altamente recomendado, mas todo o resto pode ser simples assim, a menos que você tenha uma necessidade específica. Exatamente que tipo de jogo você está fazendo?
fonte
Isso definitivamente não é verdade. A mensagem passa pela rede enquanto o receptor renderiza o quadro atual. O atraso da rede está preso a um número inteiro de quadros no lado do cliente; sim, mas se o cliente tem tão poucos FPS que isso é um grande problema, eles têm problemas maiores.
fonte
As comunicações de rede devem ser em lote. Você deve se esforçar para que um único pacote envie cada tick de jogo (que geralmente ocorre quando um quadro é renderizado, mas realmente deve ser independente).
Suas entidades de jogo conversam com o subsistema de rede (NSS). O NSS agrupa mensagens, ACKs, etc. e envia alguns (espero que um) pacotes UDP de tamanho ideal (~ 1500 bytes geralmente). O NSS emula pacotes, canais, prioridades, reenvia, etc., enquanto envia apenas pacotes UDP únicos.
Leia o gaffer no tutorial dos jogos ou use o ENet, que implementa muitas das idéias de Glenn Fiedler.
Ou você pode simplesmente usar o TCP se o seu jogo não precisar de reações de contração. Todos os problemas de lote, reenvio e ACK desaparecem. Você ainda deseja que um NSS gerencie a largura de banda e os canais, no entanto.
fonte
Não ignore completamente a capacidade de resposta. Há pouco a ganhar com a adição de outra latência de 40ms a pacotes já atrasados. Se você estiver adicionando alguns quadros (a 60fps), atrasará o processamento de uma posição e atualizará outros dois quadros. É melhor aceitar pacotes rapidamente e processá-los rapidamente, para melhorar a precisão da simulação.
Consegui otimizar a largura de banda pensando nas informações mínimas de estado necessárias para representar o que é visível na tela. Depois, observe cada bit de dados e escolha um modelo para ele. As informações de posição podem ser expressas como valores delta ao longo do tempo. Você pode usar seus próprios modelos estatísticos para isso e passar horas depurando-os, ou pode usar uma biblioteca para ajudá-lo. Prefiro usar o modelo de ponto flutuante da biblioteca DataBlock_Predict_Float Torna realmente fácil otimizar a largura de banda usada para o gráfico da cena do jogo.
fonte