Unreal Engine (UE) のネットワークのレプリケーションは、信頼性の高い通信方法と信頼性の低い通信方法の組み合わせを使用して、サーバーと接続済みクライアント間で情報を転送します。「信頼性の高い」通信は、受信するマシンが認証するまで他のすべてのネットワーク通信を停止し、送信し続けます。「信頼性の低い」通信は、受信するマシンが受信を確認しない場合、現在のネットワーク ティック中は送信したあと再送信しません。
アクタのプロパティとリモート プロシージャ コール (RPC) に関して、この通信の相対的な順序とゲーム コードで考慮するために何ができるかについて、何が保証されるかを理解することが大切です。このページでは、UE のレプリケーション システムが何を保証するか、そして同じく重要な、何を保証しないのかについて説明します。
アクタのプロパティ
アクタのプロパティの更新は信頼性が低く、1 つのまとまりで送信されます。これは、他のすべての RPC のあと、キューに入れられた RPC の前に送信される単一の信頼性の低い RPC と考えることができます。キューに入れられた RPC の詳細については、このページの 「強制キュー セクションを参照してください。
順序を使用したレプリケート
異なるレプリケート済み変数の OnRep (RepNotify) コールバック間には決定的な順序はありません。クライアントに対するコールの順序はダーティーとマークされた変数またはメモリ内のその宣言場所とは関係ありません。複数の変数間に信頼性の高い順序が必要な場合、構造体に一緒に保存することを推奨します。
ゲームにおいてアクタのプロパティ レプリケーションの順序が重要な場合、フレームごとのプロパティ更新をトラックするため OnRep を実装する必要があるかもしれません。レプリケートされた値が受信され、その OnRep が呼び出されたあと、UObject::PostRepNotifies
関数で変更を処理できます。特定の受信した値を使用する準備ができるまで、それぞれの OnRep に保存する必要もあるかもしれません。
リモート プロシージャ コール
Unreal Engine のレプリケーション システムは、可能な限り確実に RPC を実行するため、ネットワーキングの弊害を心配することなくゲームプレイ システムを構築できます。
アクタ間の順序
複数のアクタにまたがる RPC のオリジナル呼び出し順が保存され、リモート マシンに再適用される仕組みはありません。送信マシンの RPC 呼び出し順の次の例について検討してください。
AActor* MyActor;
AActor* OtherActor;
// 有効な MyActor ポインタ
MyActor->ClientRPC1();
OtherActor->ClientRPC2();
MyActor->ClientRPC3();
この例では、受信マシンの RPC の実行順は決定論的「ではなく」、RPC は受信マシンで以下のどのような組み合わせでも実行できます。
RPC1 --> RPC2 --> RPC3
RPC1 --> RPC3 --> RPC2
RPC2 --> RPC1 --> RPC3
RPC2 --> RPC3 --> RPC1
RPC3 --> RPC1 --> RPC2
RPC3 --> RPC2 --> RPC1
アクタ内の順序
レプリケーション システムは、同じアクタでの信頼性の高い RPC 呼び出し順を保証します。RPC が受信マシンで実行される順序は送信マシンで呼び出される順序と同じです。送信マシンの呼び出し順が以下の場合:
AActor* MyActor;
// 有効な MyActor ポインタ
MyActor->ClientReliableRPC1();
MyActor->ClientReliableRPC2();
MyActor->ClientReliableRPC3();
受信マシンは RPC を必ず以下の順序で実行します。
RPC1 --> RPC2 --> RPC3
アクタとサブオブジェクト間の順序
受信マシンでの RPC 実行順は、アクタとそのサブオブジェクトに呼び出されるすべての RPC で尊重されます。たとえば、送信マシンが以下を送信する場合:
AActor* MyActor;
// 有効な MyActor ポインタ
MyActor->RPC1();
MyActor->SubObject1->RPC2();
MyActor->SubObject2->RPC3();
MyActor->RPC4();
受信マシンの実行順は以下のとおりになります。
RPC1 --> RPC2 --> RPC3 --> RPC4
信頼性の低い順序と信頼性の高い順序の比較
信頼性の低い RPC と信頼性の高い RPC 間の RPC 実行の順序は保持されているように見えるかもしれませんが、決して保証されたものではありません。パケット損失またはパケットの順序並べ替えが起こらない場合、信頼性の低いものと信頼性の高いものの間の実行順序は、受信マシンでも送信マシンでも同じです。送信マシンの RPC 呼び出し順の次の例について検討してください。
AActor* MyActor;
// 有効な MyActor ポインタ
MyActor->ClientReliableRPC1();
MyActor->ClientUnicastUnreliableRPC2();
MyActor->ClientReliableRPC3();
パケット損失またはパケットの順序並べ替えが起こらない場合、受信マシンが以下の順序で RPC を実行することが可能です。
RPC1 --> RPC2 --> RPC3
RPC1
がドロップされたか、順序を並べ替えらえた個別のパケットである場合、受信マシンは以下のとおり実行します。
RPC2 --> RPC1 --> RPC3
RPC2
がドロップされた個別のパケットである場合、受信マシンは以下のとおり実行します。
RPC1 --> RPC3
この最後の場合では、RPC2
は信頼性が低いため、受信マシンにドロップされ、決して実行されることはありません。
信頼性が低い RPC2
が RPC3
のあとに実行されるシナリオがあってはなりません。RPC2
を含むパケットの順序が並べ替えられ、RPC3
よりもあとに届く場合、受信時に無視されます。
マルチキャスト順序とユニキャスト順序の比較
UE のレプリケーション システムはマルチキャスト RPC とユニキャスト RPC 間の呼び出し順を常に保持するとは限らないため、マルチキャスト RPC 順序はより複雑です。
マルチキャストの信頼性が高い
信頼性が高いマルチキャスト RPC と信頼性が高い他のユニキャスト RPC 間の呼び出し順は保持されます。たとえば、送信マシンにこの順序で以下の関数が呼び出された場合:
MyActor->MulticastReliableRPC1();
MyActor->UnicastReliableRPC2();
MyActor->UnicastReliableRPC3();
MyActor->MulticastReliableRPC4();
受信マシンは RPC を以下の順序で実行します。
RPC1 --> RPC2 --> RPC3 --> RPC4
信頼性の低い RPC3
の順序は決定論的ではなく、先に実行されることもあれば、まったく実行されないこともあることを忘れないでください。
マルチキャストの信頼性が低い
信頼性の低いマルチキャストは、他のユニキャストと信頼性の高いマルチキャスト間の呼び出し順を決して保持しません。たとえば、送信マシンにこの順序で以下の RPC が呼び出された場合:
MyActor->MulticastUnreliableRPC1();
MyActor->UnicastReliableRPC2();
MyActor->MulticastUnreliableRPC3();
MyActor->UnicastUnreliableRPC4();
受信マシンは RPC を以下の順序で実行します。
RPC2 --> RPC4 --> RPC1 --> RPC3
RPC1
と RPC3
は信頼性の低いマルチキャスト RPC であるため、キューに入れられ、最後にシリアル化されます。つまり、ユニキャストが最初に実行され、信頼性の低いマルチキャストが最後に実行されます。ドロップされた、信頼性の低いユニキャスト RPC に関するルールもここで適用されます。
RPC2
がドロップされた、または順序を並べ替えらえた個別のパケットである場合、受信マシンは RPC を以下のとおり実行します。
RPC1 --> RPC3 --> RPC2 --> RPC4
RPC 送信ポリシー
RPC に、RPC の順序に影響する明示的な送信ポリシーを割り当てることが可能です。そうするには ERemoteFunctionSendPolicy
を指定します。RPC 送信ポリシーの詳細については、「RPC について」 のドキュメントを参照してください。
強制送信
ERemoteFunctionSendPolicy::ForceSend
ポリシーのある RPC は信頼性の低いマルチキャスト RPC の順序を変更し、キューに入れられないようにします。以下はその一例です。
MyActor->ForceSendMulticastUnreliableRPC1();
MyActor->UnicastReliableRPC2();
MyActor->MulticastUnreliableRPC3();
MyActor->UnicastUnreliableRPC4();
クライアントはこれらの RPC を以下の順序で実行します。
RPC1 --> RPC2 --> RPC4 --> RPC3
強制キュー
ERemoteFunctionSendPolicy::ForceQueue
ポリシーのある RPC は、他の ForceQueue
RPC と信頼性の低いマルチキャストを除く呼び出し順を尊重しません。以下はその一例です。
MyActor->ForceQueueRPC1();
MyActor->UnicastReliableRPC2();
MyActor->MulticastUnreliableRPC3();
MyActor->UnicastUnreliableRPC4();
クライアントはこれらの RPC を以下の順序で実行します。
RPC2 --> RPC4 --> RPC1 --> RPC3
RPC とアクタ プロパティ間の順序
RPC の実行間の順序とレプリケートされたプロパティの更新されるタイミングを理解することも重要です。この場合、以下のルールが適用されます。
- RPC は最初に実行される。
- プロパティは 2 番目に更新される。
- プロパティ更新は単一の信頼性の低いデータ ブロックとして送信される。
ペイロードのまとまりは以下のとおり構成されます。
- キューに入っていない RPC がシリアル化される。
- レプリケートされたプロパティ データがシリアル化される。
- キューに入った RPC がシリアル化される。
RPC 内から書き込まれたレプリケートされた変数を消失し、未処理のプロパティ更新によって即時に上書きされる可能性があります。
信頼性の低いマルチキャスト RPC は呼び出しサイトでキューに入れられ、常に最後にシリアル化されるため、このルールの例外です。つまり、信頼性の低いマルチキャスト RPC はプロパティ更新が適用された「あとで」実行されます。
以下はその一例です。
MyActor->ReliableRPC1();
MyActor->bReplicatedVar1 = true
MyActor->MulticastUnreliableRPC2();
MyActor->bReplicatedVar2 = true;
MyActor->ReliableRPC3();
リモート マシンは以下の順序で実行します。
RPC1 --> RPC3 --> Var1 && Var2 --> RPC2
以下は、RPC が混ざったプロパティ更新のもう 1 つの例です。
MyActor->ReliableRPC1();
MyActor->bReplicatedVar1 = true
MyActor->MulticastUnreliableRPC2();
MyActor->bReplicatedVar2 = true;
MyActor->ReliableRPC3();
プロパティ更新がドロップされると仮定すると、受信マシンは RPC とプロパティ更新を以下の順序で実行します。
RPC1 --> RPC3 --> RPC2
// 次の更新のあとで
Var1 && Var2
信頼性の高い RPC1 のみがドロップされる別のシナリオでは、受信マシンでの実行は以下のようになります。
Var1 && Var2 --> RPC2 --> RPC1 --> RPC3
信頼性の低い RPC を使用してゲームプレイ コードをテストする
信頼性の低い RPC を使用してレプリケートされたコードを作成中、またはそれに依存している場合、信頼性の低い RPC がドロップされるよう強制し、システムがどのように反応するかを確認することをお勧めします。問題のあるネットワーク状態をエミュレーションしてこれを実行する方法の詳細については、「ネットワーク エミュレーションを使用する」 のドキュメントを参照してください。