Dans cet article, nous considérerons le transfert de données entre le serveur et le client dans Unreal Engine 4 implémenté en C ++ . Au tout début, pour une personne qui n'a pas d'éducation spécialisée, cela semble être quelque chose d'incompréhensiblement compliqué. Malgré le grand nombre d'exemples et d'analyses, il m'a été personnellement très difficile de dresser un tableau complet de ce processus. Mais, lorsque la quantité critique d'informations obtenues grâce à la lecture et aux tests effectués a été atteinte, on a compris comment tout cela fonctionnait.
Tout d'abord, vous devez comprendre clairement que tous les objets du jeu (à de rares exceptions près) peuvent avoir plusieurs copies. L'objet d'origine se trouve sur le serveur. L'original est toujours là. Des copies peuvent exister sur les Clients, mais pas du tout, c'est-à-dire ils ne le sont peut-être pas. Tous les événements significatifs se produisent sur le serveur, et c'est lui qui décide qui doit le savoir et qui ne le sait pas.
Tout ce qui se passe sur le Client n'est connu de personne sauf du Client. |
ExempleNous commençons le jeu avec deux clients. Il y a un cube sur la scène. L'original de ce cube se trouve sur le serveur. Il est le plus important. La première copie sera située sur le premier client, et la seconde - sur le deuxième client. Si nous faisons quelque chose avec une copie de l'objet sur l'un de leurs clients, l'original n'en souffrira pas . Le changement sera purement local, uniquement pour ce Client. Si vous changez l'original, il y a 2 scénarios principaux:
- Les copies des clients resteront inchangées.
- Des copies des clients seront synchronisées avec l'original.
Maintenant que les règles de base du jeu sont claires, nous pouvons considérer quelles options de transmission d'informations entre le Serveur et le Client s'offrent à nous. Je connais 3 façons, mais nous ne considérerons que les deux premières, car le troisième vous permet de transférer n'importe quoi et n'importe où, et ne s'applique que si vous êtes limité par les limitations des deux premiers.
- Réplication
- RPC ( appels de procédure à distance ).
- TCP
Réplication
La première chose à accepter sans condition:
La réplication est un chemin à sens unique et ne fonctionne que du serveur vers le client. |
La deuxième chose que vous devez savoir:
Seuls les objets ou les variables de classe peuvent être répliqués. |
Et troisièmement, une condition importante:
La réplication se produit uniquement lorsqu'une modification s'est produite sur le serveur. |
Si dans
Blueprint, nous
cochons simplement les bons endroits, alors
C ++ n'est pas beaucoup plus compliqué. L'essentiel
est de ne pas oublier d'inclure
#include "UnrealNetwork.h" .
Tout d'abord, envisagez la réplication d'objet.
Dans le constructeur, nous écrivons:
bReplicates = true;
Si nous voulons reproduire le mouvement:
bReplicateMovement = true;
Si vous devez répliquer le composant connecté:
Component->SetReplicates(true);
Une description complète peut être trouvée ici .
Avec la réplication variable, les choses sont un peu plus intéressantes.
Commençons par le fichier d'en-tête .h .
Vous pouvez simplement répliquer la variable:
UPROPERTY(Replicated) bool bMyReplicatedVariable;
Ou vous pouvez exécuter une fonction côté client si la variable a été répliquée. Peu importe la valeur que prend la variable. Le fait de son changement est important.
UPROPERTY(ReplicatedUsing = OnRep_MySomeFunction) TArray<float> MyReplicatedArray; UFUNCTION() void OnRep_MySomeFunction();
L'exécution d'une fonction n'est qu'une des façons d'envoyer des commandes depuis le serveur. Il est très important de savoir que la réplication des tableaux ne se produit pas entièrement, mais uniquement la partie modifiée. Ainsi, avec une seule variable, vous pouvez envoyer de nombreuses commandes à partir du serveur, en analysant le tableau en éléments et les éléments en bits.
Passons maintenant au .cpp
Nous écrivons des conditions de réplication:
void AMySuperActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AMySuperActor, bMyReplicatedVariable); DOREPLIFETIME_CONDITION(AMySuperActor, MyReplicatedArray, COND_OwnerOnly); }
La première variable bMyReplicatedVariable a été répliquée inconditionnellement sur tous les clients à la fois, tandis que la deuxième, MyReplicatedArray , a été mise à jour uniquement pour le client propriétaire de l'objet AMySuperActor , sauf si, bien sûr, elle en a été déclarée une.
Une liste complète des conditions possibles peut être trouvée ici .
RPC ( appels de procédure à distance )
Contrairement à la réplication, cette méthode de transfert de données fonctionne dans les deux sens, mais est plus coûteuse. Pour l'utiliser, il vous suffit de vous connecter #include "UnrealNetwork.h" .
Une caractéristique importante est que vous pouvez transférer des variables à l'aide de la méthode RPC . |
Tout d'abord, il faut dire que les RPC sont livrés avec accusé de réception de
Reliable et sans
Unreliable . Si dans le premier cas l'expéditeur ne se calme pas jusqu'à ce qu'il soit convaincu que le colis a été livré (et l'enverra encore et encore s'il n'y a pas de nouvelles réciproques), alors dans le second, l'expéditeur ne se soucie pas de savoir si quelqu'un a reçu le colis ou non. Envoyé et oublié.
Dans la mesure du possible - utilisez
Non fiable . Habituellement, cette méthode convient aux informations peu importantes ou aux données fréquemment mises à jour. Lorsque cela est impossible, dans le cas d'envoi du serveur vers le client, nous essayons de faire avec la réplication avec un appel de fonction, comme indiqué ci-dessus.
Ainsi, pour envoyer notre colis du Client au Serveur, vous devez enregistrer trois fonctions:
UFUNCTION(Reliable, Server, WithValidation) void ServerTestFunction(float MyVariable); void ServerTestFunction_Implementation(float MyVariable); bool ServerTestFunction_Validate(float MyVariable);
Fiable - un colis avec accusé de réception.
Serveur - envoi du client au serveur.
WithValidation - le package n'est ouvert par le destinataire que si les conditions décrites dans la fonction bool ServerTestFunction_Validate (float MyVariable) sont remplies . Autrement dit, si la fonction renvoie true . Ce paramètre est requis uniquement pour les fonctions exécutées sur le serveur.
ServerTestFunction (float MyVariable) - cette fonction est appelée par le client s'il veut envoyer quelque chose au serveur. En général, il n'est même pas nécessaire de le décrire en .cpp .
ServerTestFunction_Implementation (float MyVariable) - cette fonction sera appelée directement sur le serveur uniquement si ...
ServerTestFunction_Validate (float MyVariable) - cette fonction est exécutée sur le serveur, et si true est retourné, ServerTestFunction_Implementation (float MyVariable) sera appelé.
Pour envoyer un package du serveur au client, si nous ne sommes catégoriquement pas satisfaits de l'utilisation de la réplication, en substance, seul le serveur passe au client :
UFUNCTION(Reliable, Client, WithValidation)
Les noms des fonctions, en principe, peuvent être arbitraires, mais ils doivent généralement refléter où va le paquet. Pour notre commodité.
UFUNCTION(Reliable, Client, WithValidation) void ClientTestFunction(float MyVariable); void ClientTestFunction_Implementation(float MyVariable); bool ClientTestFunction_Validate(float MyVariable);
Sinon, cela fonctionne exactement de la même manière que dans l'exemple précédent, en tenant compte du fait que cette fois le package va du serveur au client.
Il existe une autre option pour l'envoi à partir du serveur, lorsque le package est envoyé à tous les clients à la fois.
UFUNCTION(Reliable, NetMulticast, WithValidation) void NetMulticastTestFunction(); void NetMulticastTestFunction_Implementation(); bool NetMulticastTestFunction_Validate();
N'abusez pas de cette option. Réfléchissez à la façon dont vous pourriez gérer la réplication.
Pour Client et NetMulticast, la validation est facultative |
Exemple d'implémentation d'une requête serveur 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; }
Et enfin, un lien vers le manuel, qui doit être lu.
Compendium réseau 'Unreal Engine 4'
C'est tout ce que vous devez savoir sur la communication entre le serveur et le client afin de passer à la section suivante, où nous noterons la réplication de l'inventaire et examinerons comment y apporter des modifications correctement.
PS Si vous constatez des inexactitudes ou des erreurs, veuillez écrire dans les commentaires.