Neste artigo, consideraremos a transferência de dados entre o servidor e o cliente no Unreal Engine 4 implementada em C ++ . No começo, para uma pessoa que não tem educação especializada, isso parece ser algo incompreensivelmente complicado. Apesar do grande número de exemplos e análises, foi pessoalmente muito difícil para mim montar uma imagem completa desse processo. Mas, quando a quantidade crítica de informações obtidas através da leitura e dos testes realizados foi alcançada, chegou-se a um entendimento de como tudo isso funciona.
Primeiro de tudo, você precisa entender claramente que todos os objetos no jogo (com raras exceções) podem ter várias cópias. O objeto original está localizado no servidor. O original está sempre lá. Podem existir cópias nos Clientes, mas não existem, ou seja, eles podem não ser. Todos os eventos significativos ocorrem no servidor, e é ele quem decide quem precisa saber e quem não sabe.
Tudo o que acontece no cliente não é conhecido por ninguém, exceto o cliente. |
ExemploComeçamos o jogo com dois clientes. Há um cubo no palco.O original deste cubo está localizado no servidor. Ele é o mais importante. A primeira cópia estará localizada no primeiro cliente e a segunda - no segundo cliente. Se fizermos algo com uma cópia do objeto em qualquer um de seus clientes, o original não sofrerá . A mudança será puramente local, apenas para este cliente. Se você alterar o original, existem 2 cenários principais:
- As cópias dos clientes permanecerão inalteradas.
- Cópias dos clientes serão sincronizadas com o original.
Agora que as regras básicas do jogo são claras, podemos considerar quais opções de transmissão de informações entre o Servidor e o Cliente estão disponíveis para nós. Conheço três maneiras, mas consideraremos apenas as duas primeiras, porque o terceiro permite transferir qualquer coisa e em qualquer lugar, e só se aplica se você estiver limitado pelas limitações dos dois primeiros.
- Replicação
- RPC ( chamadas de procedimento remoto ).
- TCP
Replicação
A primeira coisa a aceitar incondicionalmente:
A replicação é uma via de mão única e funciona apenas do servidor para o cliente. |
A segunda coisa que você precisa saber:
Somente objetos ou variáveis de classe podem ser replicados. |
E terceiro, uma condição importante:
A replicação ocorre apenas quando uma alteração ocorre no servidor. |
Se no
Blueprint marcamos os lugares certos, o
C ++ não é muito mais complicado. O principal
é não esquecer de incluir
#include "UnrealNetwork.h" .
Primeiro, considere a replicação do objeto.
No construtor, escrevemos:
bReplicates = true;
Se queremos replicar o movimento:
bReplicateMovement = true;
Se você precisar replicar o componente conectado:
Component->SetReplicates(true);
Uma descrição completa pode ser encontrada aqui .
Com a replicação variável, as coisas são um pouco mais interessantes.
Vamos começar com o arquivo de cabeçalho .h .
Você pode simplesmente replicar a variável:
UPROPERTY(Replicated) bool bMyReplicatedVariable;
Ou você pode executar alguma função no lado do cliente se a variável tiver sido replicada. Não importa qual valor a variável assume. O fato de sua mudança é importante.
UPROPERTY(ReplicatedUsing = OnRep_MySomeFunction) TArray<float> MyReplicatedArray; UFUNCTION() void OnRep_MySomeFunction();
A execução de uma função é apenas uma das maneiras de enviar comandos do servidor. É muito importante saber que a replicação de matrizes não ocorre totalmente, mas apenas a parte alterada. Assim, com apenas uma variável, você pode enviar muitos comandos do servidor, analisando a matriz em elementos e elementos em bits.
Agora vamos para .cpp
Escrevemos condições de replicação:
void AMySuperActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AMySuperActor, bMyReplicatedVariable); DOREPLIFETIME_CONDITION(AMySuperActor, MyReplicatedArray, COND_OwnerOnly); }
A primeira variável bMyReplicatedVariable foi replicada incondicionalmente a todos os clientes de uma só vez, enquanto a segunda, MyReplicatedArray , foi atualizada apenas para o cliente que possui o objeto AMySuperActor , a menos que, é claro, tenha sido declarado.
Uma lista completa de possíveis condições pode ser encontrada aqui .
RPC ( chamadas de procedimento remoto )
Esse método de transferência de dados, diferentemente da replicação, funciona nos dois sentidos, mas é mais caro. Para usá-lo, você só precisa conectar #include "UnrealNetwork.h" .
Um recurso importante é que você pode transferir variáveis usando o método RPC . |
Primeiro, deve-se dizer que os RPCs vêm com o aviso de recebimento de
Confiável e sem
Confiável . Se, no primeiro caso, o Remetente não se acalmar até que esteja convencido de que a encomenda foi entregue (e a enviará repetidamente se não houver notícias recíprocas), então, no segundo, o Remetente não se importará se alguém recebeu a encomenda ou não. Enviado e esquecido.
Sempre que possível - use Não
confiável . Normalmente, esse método é adequado para informações não muito importantes ou para dados atualizados com freqüência. Onde é impossível, no caso de enviar do servidor para o cliente, tentamos fazer a replicação com uma chamada de função, como mostrado acima.
Portanto, para enviar nosso pacote do cliente para o servidor, é necessário registrar três funções:
UFUNCTION(Reliable, Server, WithValidation) void ServerTestFunction(float MyVariable); void ServerTestFunction_Implementation(float MyVariable); bool ServerTestFunction_Validate(float MyVariable);
Confiável - um pacote com aviso de recebimento.
Servidor - enviando do cliente para o servidor.
WithValidation - o pacote é aberto pelo destinatário somente se as condições descritas na função booleana ServerTestFunction_Validate (float MyVariable) forem atendidas . Ou seja, se a função retornar verdadeira . Este parâmetro é necessário apenas para funções executadas no servidor.
ServerTestFunction (float MyVariable) - essa função é chamada pelo cliente se ele deseja enviar algo ao servidor. Em geral, nem é necessário descrevê-lo em .cpp .
ServerTestFunction_Implementation (float MyVariable) - esta função será chamada diretamente no servidor apenas se ...
ServerTestFunction_Validate (float MyVariable) - essa função é executada no servidor e, se true for retornado, ServerTestFunction_Implementation (float MyVariable) será chamado.
Para enviar um pacote do servidor para o cliente, se não estivermos categoricamente satisfeitos com o uso da replicação, em essência, apenas o servidor mudará para o cliente :
UFUNCTION(Reliable, Client, WithValidation)
Os nomes das funções, em princípio, podem ser arbitrários, mas geralmente devem refletir para onde o pacote está indo. Para nossa comodidade.
UFUNCTION(Reliable, Client, WithValidation) void ClientTestFunction(float MyVariable); void ClientTestFunction_Implementation(float MyVariable); bool ClientTestFunction_Validate(float MyVariable);
Caso contrário, ele funciona exatamente da mesma maneira que no exemplo anterior, levando em consideração o fato de que desta vez o pacote vai do servidor para o cliente.
Há outra opção para enviar do servidor, quando o pacote vai para todos os clientes ao mesmo tempo.
UFUNCTION(Reliable, NetMulticast, WithValidation) void NetMulticastTestFunction(); void NetMulticastTestFunction_Implementation(); bool NetMulticastTestFunction_Validate();
Não abuse desta opção. Pense em como você pode gerenciar a replicação.
Para Client e NetMulticast, a validação é opcional |
Exemplo de implementação de solicitação do servidor void ADreampaxActor::DoSomethingWithOtherActor(ADreampaxOtherActor * SomeOtherActor) { if (Role < ROLE_Authority) { ServerDoSomethingWithOtherActor(SomeOtherActor); return; } SomeOtherActor->Destroy(true); } void ADreampaxCharacter::ServerDoSomethingWithOtherActor_Implementation(ADreampaxOtherActor * SomeOtherActor) { DoSomethingWithOtherActor(SomeOtherActor); } bool ADreampaxCharacter::ServerDoSomethingWithOtherActor_Validate(ADreampaxOtherActor * SomeOtherActor) { return true; }
E, finalmente, um link para o manual, que deve ser lido.
Compêndio de Rede 'Unreal Engine 4'
É tudo o que você precisa saber sobre a comunicação entre o Servidor e o Cliente para prosseguir para a próxima seção, onde escreveremos a replicação do inventário e consideraremos como fazer alterações corretamente.
PS Se você notar alguma imprecisão ou erro, escreva nos comentários.