UEFNとUnreal Engine MultiplayerにおけるPlayer Location Desyncの修正方法
Multiplayer Transform Desync の悪夢
マルチプレイヤーゲームの開発者なら誰しも、netcode が「嘘」をつき始める瞬間に直面します。プレイヤーが椅子に座り、その椅子がマップ上を移動するシーケンスをスクリプトしたとします。サーバー上では完璧に見えます。しかし、2つ目のクライアントを立ち上げると、その幻想は打ち砕かれます。クライアントAには椅子に乗っている自分が正しく見えていますが、クライアントBには、椅子だけが移動し、クライアントAは開始地点の空中に固定されたままに見えるのです。
この特定の現象 — uefn player location desync — は、アタッチされたプレイヤーを保持するプロップに対して MoveTo コマンドを使用した際によく発生する問題です。サーバーは正しい絶対座標を登録していますが、simulated proxies(他のクライアント上のプレイヤー表現)が親プロップから更新された transform を継承できないために起こります。
UEFN でカスタム体験を構築している場合でも、Unreal Engine 5 で独立した dedicated server を設計している場合でも、アタッチメントの replication がなぜ失敗するのかを理解することは不可欠です。このチュートリアルでは、Network Dormancy の仕組み、なぜアタッチメントが client-side prediction を壊すのか、そして replication graph にワールド状態を強制的に認識させる方法を解説します。
なぜアタッチメントが Network Updates を妨げるのか
デシンクを修正するには、Unreal Engine が actor replication を階層的にどのように処理するかを理解する必要があります。
キャラクターがプロップにアタッチされると、その transform は親アクターに対する相対的なものになります。帯域幅を節約するため、Unreal Engine は Network Dormancy という概念を積極的に利用します。
プレイヤーが座って移動入力を止めると、サーバーの replication graph はプレイヤーアクターを dormant(休眠状態)としてフラグを立てることがあります。サーバーは「プレイヤーは独立して動いていないので、更新を送る必要はない。動いている椅子の更新だけを送ればいい」と判断してしまうのです。
デシンクの背後にある数値
- Server Tick Rate: UEFN では通常 30Hz。
- Prop NetUpdateFrequency: デフォルトで秒間 100 回の更新。
- Character MinNetUpdateFrequency: 入力がない場合、秒間 2.0 回まで低下することがあります。
椅子を MoveTo で動かすと、椅子の transform は 30Hz で更新されます。しかし、アタッチされたプレイヤーが休眠状態だと、他のクライアントは相対位置を更新するための RPC (Remote Procedure Call) を受信しません。その結果、大きな視覚的デシンクが発生します。
ステップ 1: UEFN Verse での回避策
UEFN のバグレポートでは、「椅子から降りると直る」というヒントが示されています。これは、movement mode を Walking に戻すことで、サーバーが Network Dormancy をフラッシュし、絶対座標の更新を全クライアントに強制的に送信するためです。
Verse では、プレイヤーを降ろさずにプログラムでこの「フラッシュ」を再現できます。マイクロテレポートを使用して replication graph を強制的に起こします。
# [Verse コードはオリジナルを維持]
同じ座標に TeleportTo を呼び出すことで、C++ エンジンの TeleportPhysics フラグを立て、そのアクターの client-side prediction を完全にリセットします。
ステップ 2: ネイティブ Unreal Engine C++ での修正
ソースコードにアクセスできる場合は、networking API を直接操作して dormancy を管理できます。
// UE5 C++ での強制ネットワーク更新例
// [C++ コードはオリジナルを維持]
ステップ 3: バックエンドへの状態保存
リアルタイムのデシンク修正は半分に過ぎません。永続的なワールドを持つゲームでは、データベースに「正しい」絶対座標を保存する必要があります。強制的なネット更新の後に、server-authoritative な transform をクエリするようにしてください。
バックエンドの構築には時間がかかります。horizOn はゲーム開発者向けに設計された Backend-as-a-Service で、リアルタイムの永続化機能を提供しています。
5つのベストプラクティス
- クライアントのアタッチ状態を信用しない: 常に server-authoritative な RPC で検証する。
- 乗り物の NetDormancy を手動管理する: 移動中は
FlushNetDormancyを明示的に呼ぶ。 - アタッチ階層を浅く保つ: プレイヤー -> 椅子 -> 列車 -> 移動プラットフォームのような深い階層を避ける。
- 重要な状態変化には信頼性の高い RPC を使う: 乗り降りには NetMulticast を使用する。
- 降りる際の Transform を検証する: サーバー側で座標の妥当性をチェックする。
結論
マルチプレイヤーのデシンクは、ネットワーク最適化の結果として起こることがほとんどです。Network Dormancy の影響を理解すれば、Verse や C++ で強制的に更新をかけることができます。replication graph を制御し、サーバー権限を徹底しましょう。
バックエンドの拡張にお困りですか? horizOn を無料でお試しください。