UE4 | Ausrüstung für den Mehrspielermodus # 5 | Informationsübertragung zwischen Server und Client


In diesem Artikel betrachten wir die Datenübertragung zwischen dem Server und dem Client in Unreal Engine 4, die in C ++ implementiert ist. Für eine Person ohne Fachausbildung scheint dies zu Beginn etwas unverständlich Kompliziertes zu sein. Trotz der Vielzahl von Beispielen und Analysen war es für mich persönlich sehr schwierig, ein vollständiges Bild dieses Prozesses zusammenzustellen. Als jedoch die kritische Menge an Informationen erreicht war, die durch Lesen und durchgeführte Tests erhalten wurden, wurde verstanden, wie dies alles funktioniert.




Zunächst müssen Sie klar verstehen, dass alle Objekte im Spiel (mit seltenen Ausnahmen) mehrere Kopien haben können. Das ursprüngliche Objekt befindet sich auf dem Server. Das Original ist immer da. Kopien können auf den Clients vorhanden sein, aber überhaupt nicht, d. H. sie können nicht sein. Alle wichtigen Ereignisse treten auf dem Server auf, und er entscheidet, wer darüber Bescheid wissen muss und wer nicht.

Alles, was auf dem Client passiert, ist niemandem außer dem Client bekannt.

Beispiel

Wir starten das Spiel mit zwei Clients. Auf der Bühne befindet sich ein Cube. Das Original dieses Cubes befindet sich auf dem Server. Er ist der wichtigste. Die erste Kopie befindet sich auf dem ersten Client und die zweite auf dem zweiten Client. Wenn wir bei einem ihrer Kunden etwas mit einer Kopie des Objekts tun, leidet das Original nicht . Die Änderung ist nur für diesen Kunden rein lokal. Wenn Sie das Original ändern, gibt es zwei Hauptszenarien:


  1. Kopien von Kunden bleiben unverändert.
  2. Kopien von Kunden werden mit dem Original synchronisiert.



Nachdem die Grundregeln des Spiels klar sind, können wir überlegen, welche Optionen für die Übertragung von Informationen zwischen dem Server und dem Client uns zur Verfügung stehen. Ich kenne 3 Möglichkeiten, aber wir werden nur die ersten beiden betrachten, weil Die dritte Möglichkeit ermöglicht es Ihnen, alles und überall zu übertragen. Sie gilt nur, wenn Sie durch die Einschränkungen der ersten beiden eingeschränkt sind.


  1. Replikation
  2. RPC ( Remote Procedure Calls ).
  3. TCP


    Replikation


Das erste, was bedingungslos akzeptiert werden muss:

Die Replikation ist eine Einbahnstraße und funktioniert nur vom Server zum Client.
Das zweite, was Sie wissen müssen:
Es können nur Objekte oder Klassenvariablen repliziert werden.
Und drittens eine wichtige Bedingung:
Die Replikation erfolgt nur, wenn auf dem Server eine Änderung vorgenommen wurde.

Wenn wir in Blueprint nur die richtigen Stellen ankreuzen, ist C ++ nicht viel komplizierter. Die Hauptsache ist nicht zu vergessen, #include "UnrealNetwork.h" einzuschließen .

Betrachten Sie zunächst die Objektreplikation.
Im Konstruktor schreiben wir:


bReplicates = true; 

Wenn wir die Bewegung wiederholen wollen:


 bReplicateMovement = true; 

Wenn Sie die verbundene Komponente replizieren müssen:


 Component->SetReplicates(true); 

Eine vollständige Beschreibung finden Sie hier .


Bei der variablen Replikation sind die Dinge etwas interessanter.
Beginnen wir mit der .h- Header-Datei.
Sie können die Variable einfach replizieren:


 UPROPERTY(Replicated) bool bMyReplicatedVariable; 

Oder Sie können eine Funktion auf der Clientseite ausführen, wenn die Variable repliziert wurde. Es spielt keine Rolle, welchen Wert die Variable annimmt. Die Tatsache seiner Änderung ist wichtig.


 UPROPERTY(ReplicatedUsing = OnRep_MySomeFunction) TArray<float> MyReplicatedArray; UFUNCTION() void OnRep_MySomeFunction(); 

Das Ausführen einer Funktion ist nur eine der Möglichkeiten, Befehle vom Server zu senden. Es ist sehr wichtig zu wissen, dass die Replikation von Arrays nicht vollständig erfolgt, sondern nur der geänderte Teil. Mit nur einer Variablen können Sie also viele Befehle vom Server senden und das Array in Elemente und Elemente in Bits analysieren.


Fahren wir nun mit .cpp fort
Wir schreiben Replikationsbedingungen:


 void AMySuperActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AMySuperActor, bMyReplicatedVariable); DOREPLIFETIME_CONDITION(AMySuperActor, MyReplicatedArray, COND_OwnerOnly); } 

Die erste Variable bMyReplicatedVariable wurde bedingungslos auf alle Clients gleichzeitig repliziert, während die zweite Variable , MyReplicatedArray , nur für den Client aktualisiert wurde, dem das AMySuperActor- Objekt gehört, es sei denn, es wurde natürlich als eines deklariert.


Eine vollständige Liste möglicher Bedingungen finden Sie hier .




RPC ( Remote Procedure Calls )


Diese Methode der Datenübertragung funktioniert im Gegensatz zur Replikation in beide Richtungen, ist jedoch teurer. Um es zu verwenden, müssen Sie nur #include "UnrealNetwork.h" verbinden .

Eine wichtige Funktion ist, dass Sie Variablen mit der RPC- Methode übertragen können.
Zunächst muss gesagt werden, dass RPCs mit einer Empfangsbestätigung von Reliable und ohne Unzuverlässigkeit geliefert werden . Wenn sich der Absender im ersten Fall nicht beruhigt, bis er überzeugt ist, dass das Paket zugestellt wurde (und es immer wieder versendet, wenn keine gegenseitigen Nachrichten vorliegen), ist es dem Absender im zweiten Fall egal, ob jemand das Paket erhalten hat oder nicht. Gesendet und vergessen.
Wenn möglich - Verwenden Sie Unzuverlässig . Normalerweise eignet sich diese Methode für nicht sehr wichtige Informationen oder für häufig aktualisierte Daten. Wo dies beim Senden vom Server an den Client nicht möglich ist, versuchen wir, die Replikation mit einem Funktionsaufruf durchzuführen, wie oben gezeigt.

Um unser Paket vom Client an den Server zu senden, müssen Sie drei Funktionen registrieren:


 UFUNCTION(Reliable, Server, WithValidation) void ServerTestFunction(float MyVariable); void ServerTestFunction_Implementation(float MyVariable); bool ServerTestFunction_Validate(float MyVariable); 

Zuverlässig - ein Paket mit Empfangsbestätigung.
Server - Senden vom Client an den Server.
WithValidation - Das Paket wird vom Empfänger nur geöffnet, wenn die in der Funktion bool ServerTestFunction_Validate (float MyVariable) beschriebenen Bedingungen erfüllt sind. Das heißt, wenn die Funktion true zurückgibt. Dieser Parameter ist nur für Funktionen erforderlich, die auf dem Server ausgeführt werden.
ServerTestFunction (float MyVariable) - Diese Funktion wird vom Client aufgerufen, wenn er etwas an den Server senden möchte. Im Allgemeinen ist es nicht einmal erforderlich, es in .cpp zu beschreiben.
ServerTestFunction_Implementation (float MyVariable) - Diese Funktion wird nur dann direkt auf dem Server aufgerufen, wenn ...
ServerTestFunction_Validate (float MyVariable) - Diese Funktion wird auf dem Server ausgeführt. Wenn true zurückgegeben wird, wird ServerTestFunction_Implementation (float MyVariable) aufgerufen.


So senden Sie ein Paket vom Server an den Client: Wenn wir mit der Verwendung der Replikation grundsätzlich nicht zufrieden sind, ändert sich im Wesentlichen nur der Server zum Client :


 UFUNCTION(Reliable, Client, WithValidation) 

Die Namen der Funktionen können im Prinzip beliebig sein, sollten aber normalerweise widerspiegeln, wohin das Paket geht. Für unsere Bequemlichkeit.


 UFUNCTION(Reliable, Client, WithValidation) void ClientTestFunction(float MyVariable); void ClientTestFunction_Implementation(float MyVariable); bool ClientTestFunction_Validate(float MyVariable); 

Ansonsten funktioniert es genauso wie im vorherigen Beispiel, wobei berücksichtigt wird, dass das Paket diesmal vom Server zum Client wechselt.


Es gibt eine weitere Option zum Senden vom Server, wenn das Paket gleichzeitig an alle Clients gesendet wird.


 UFUNCTION(Reliable, NetMulticast, WithValidation) void NetMulticastTestFunction(); void NetMulticastTestFunction_Implementation(); bool NetMulticastTestFunction_Validate(); 

Missbrauche diese Option nicht. Überlegen Sie, wie Sie die Replikation verwalten können.

Für Client und NetMulticast ist die Validierung optional



Beispiel für die Implementierung einer Serveranforderung
 /*       ,     */ void ADreampaxActor::DoSomethingWithOtherActor(ADreampaxOtherActor * SomeOtherActor) { /*  ,      */ if (Role < ROLE_Authority) { /*    ,    ,      */ ServerDoSomethingWithOtherActor(SomeOtherActor); /*   ,        */ return; } /*         */ SomeOtherActor->Destroy(true); } /*      ,    ServerDoSomethingWithOtherActor(SomeOtherActor)     */ void ADreampaxCharacter::ServerDoSomethingWithOtherActor_Implementation(ADreampaxOtherActor * SomeOtherActor) { /*   ,       */ DoSomethingWithOtherActor(SomeOtherActor); } /*     ,    ServerDoSomethingWithOtherActor_Implementation(ADreampaxOtherActor * SomeOtherActor) */ bool ADreampaxCharacter::ServerDoSomethingWithOtherActor_Validate(ADreampaxOtherActor * SomeOtherActor) { /*      true,   ,   -    fasle.       */ return true; } 

Und zum Schluss noch ein Link zum Handbuch, der gelesen werden muss.
Netzwerkkompendium 'Unreal Engine 4'


Das ist alles, was Sie über die Kommunikation zwischen dem Server und dem Client wissen müssen, um mit dem nächsten Abschnitt fortzufahren, in dem wir die Replikation des Inventars aufschreiben und überlegen, wie Sie Änderungen daran vornehmen können.


PS Wenn Sie Ungenauigkeiten oder Fehler bemerken, schreiben Sie bitte in die Kommentare.

Source: https://habr.com/ru/post/de420233/


All Articles