[Tokyo DevDays  26'] Iris And Mover Networking

>100 Views

August 31, 26

スライド概要

DevDays 26' での Iris と Moverをもちいた ネットワーク対応についての講演資料です

profile-image

Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

DevDays 2026 UE5.8 マルチプレイアップデート Iris & Mover Epic Games Japan, Senior Software Engineer, Developer Relations Takashi Suzuki

2.

Production Ready Experimental Iris Mover レプリケーションの新基盤 キャラクター移動の新基盤プラグイン マルチプレイ時の移動同期の機能を持つ 既定で有効か:いいえ(モジュール単位でOpt-In) 既定で有効か:いいえ

3.

目次 Iris Mover

4.

第I部 Iris レプリケーションを、データ指向で新設計

5.

Iris とは サーバーとクライアント間のデータの受け渡しを行う 新しいレプリケーション(複製)システム 本スライドでは 今までのレプリケーションシステムを レガシーレプリケーションと呼びます ネットドライバー サーバー ワールド レプリケーション システム ネットドライバー レプリケーション システム クライアント ワールド

6.

Iris とは サーバーとクライアント間のデータの受け渡しを行う 新しいレプリケーション(複製)システム ネットドライバー サーバー ワールド Iris ネットドライバー Iris クライアント ワールド

7.

レガシーシステムの限界 スケーリング ReplicationGraph 並列化が困難 接続先毎に一から処理する 部分が多く、接続数に応じ て負荷が単純増加 大量の接続を捌く ReplicationGraphが外付け 学習、運用コストが高い チャンネル/接続にまたがる ステートが多く 複製対象への 仮想関数呼び出しが散在

8.

Irisの柱 パフォーマンス システムの分離 互換性 量子化(≒シリアライズ)を一 回だけ行い、 レプリケーションシステム からワールドやアクターを 直接参照しない 開発者への負担を最小限に 接続毎にバッチ処理 アクター数と接続数で うまくスケーリング ※シーングラフなどを見据 えた設計 タイトル側の対応は ほとんど不要

9.

レガシーレプリケーションの動作 ワールド アクター 変更検出 アクターチャンネル 優先度ソート 関連性 シリアライズ アクター x 接続先数分 ループ

10.

Iris の 動作 ワールド アクター 接続先1のデータストリーム 優先度付け フィルタ ChangeMask デルタ圧縮 接続先2のデータストリーム 情報取得 優先度付け フィルタ ChangeMask デルタ圧縮 量子化 POD (量子化済みデータ) 接続先3のデータストリーム 優先度付け フィルタ ChangeMask デルタ圧縮

11.

POD ( Plain Old Data ) Plain Old Data の Old は時間軸を示す形容詞ではなく Plain Old ~ で「ただの/なんでもない」という意味 つまりmemcpyで直接扱えるタイプのただのデータ列を表します

12.

Iris の 動作 ワールド アクター 接続先1のデータストリーム 優先度付け フィルタ ChangeMask デルタ圧縮 接続先2のデータストリーム 情報取得 優先度付け フィルタ ChangeMask デルタ圧縮 量子化 POD (量子化済みデータ) 接続先3のデータストリーム 優先度付け フィルタ ChangeMask デルタ圧縮

13.

ChangeMask の 動作 アクター (オブジェクト) 接続先1のデータストリーム PollingまたはPush シャドウステート 接続先2のデータストリーム POD (量子化済みデータ) 接続先3のデータストリーム ChangeMask

14.

ChangeMask の 動作 コピー アクター (オブジェクト) 接続先1のデータストリーム PollingまたはPush POD (量子化済みデータ) ChangeMask追加 シャドウステート 接続先2のデータストリーム 接続先3のデータストリーム ChangeMask

15.

ChangeMask の 動作 アクター (オブジェクト) 接続先1のデータストリーム PollingまたはPush シャドウステート 接続先2のデータストリーム 量子化 POD (量子化済みデータ) 接続先3のデータストリーム ChangeMask

16.

ChangeMask の 動作 アクター (オブジェクト) 接続先1のデータストリーム PollingまたはPush シャドウステート 接続先2のデータストリーム POD (量子化済みデータ) 接続先3のデータストリーム ChangeMask

17.

ChangeMask の 動作 アクター (オブジェクト) 接続先1のデータストリーム シ リ ア ラ イ PollingまたはPush ズ シャドウステート 接続先2のデータストリーム シ リ ア ラ イ POD (量子化済みデータ) ズ 参照 接続先3のデータストリーム ChangeMask

18.

NetSerializer 型や構造体をIrisで制御するためのシリアライザー ● ● ● Quantize(構造体⇔POD) Serialize(POD⇔パケット) SerializeDelta(デルタ圧縮) 非本の型、UCLASS、USTRUCTなどのほとんどのクラスは デフォルト実装を利用可能。 できる限りエンジンに任せることを推奨。 ただしレガシーシステムでNetSerializeが実装されていたクラスに 対してはNetSerializerを実装する必要がある

19.

struct FExampleNetSerializer { /** 必須 Version番号 */ static constexpr uint32 Version = 0; // 以下の各トレイトは任意。下記の既定値と異なる場合にのみ指定すればよい。 /* 少なくとも 1 つの他のシリアライザへ呼び出しを転送する意図があり、 実装可能なすべての関数を確実に実装したい場合に指定する。 */ static constexpr bool bIsForwardingSerializer = false; /* 接続固有のシリアライズが必要な場合に指定する。使用は避けること! */ static constexpr bool bHasConnectionSpecificSerialization = false; /* CollectNetReferences の実装が必要な場合に指定する。*/ static constexpr bool bHasCustomNetReference = false; /* シリアライザが動的ステートを必要とする場合に指定する。 * CloneDynamicState と FreeDynamicState の実装が必須になる。*/ static constexpr bool bHasDynamicState = false; /** 同値デルタ圧縮方式が望ましくない場合にfalse を設定する。 * 例えば、シリアライザがそのステートに対して1 ビットしか書き込まない場合など。*/ static constexpr bool bUseDefaultDelta = true; /** 必須 外部ステートのサイズとアライメントを算出し、一部の関数の既定実装を 提供する */ typedef FSomeSourceType SourceType; /** QuantizedType の typedef は任意。ただしSourceType が POD でない場合は必須。 * 指定しない場合はSourceType とみなされる。QuantizedType は POD である必要がある。 */ typedef FSomePodType QuantizedType; /** 必須 ConfigType の typedef は必須。 */ typedef FNetSerializerConfig ConfigType; /** ほぼ必須 DefaultConfig は任意だが強く推奨される。*/ inline static const ConfigType DefaultConfig; /** 必須。Serialize は、シリアライゼーションコンテキストから提供される * ビットストリームへ量子化済みデータを書き込む責務を持つ。*/ static void Serialize(FNetSerializationContext&, const FNetSerializeArgs&); /** 必須。Deserialize は、シリアライゼーションコンテキストから提供される * ビットストリームから量子化済みデータを読み取る責務を持つ。*/ static void Deserialize(FNetSerializationContext&, const FNetDeserializeArgs&); /** 任意。Serialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。*/ static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&); /** 任意。Deserialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。 */ static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&); /** 任意。 ソースデータを、高速にシリアライズできるPOD 形式へ変換する。 * 量子化はオブジェクトごと・フレームごとに高々1 回しか実行されないため、 * シリアライズを高速化する目的でやや重い計算を行うのに絶好の機会である。 */ static void Quantize(FNetSerializationContext&, const FNetQuantizeArgs&); /** 任意。ただし QuantizedType の typedef または Quantize の実装がある場合は必須。 * dequantize 関数は、量子化されたステートを妥当なソースデータ形式へ変換する責務を持つ。 * 変換結果は、量子化前の元のソースデータとおおむね等しくなる。 */ static void Dequantize(FNetSerializationContext&, const FNetDequantizeArgs&); /** 任意。あるデータが他のデータと等しいかどうかを判定する。 * 既定の実装は等価演算子を使用する。*/ static bool IsEqual(FNetSerializationContext&, const FNetIsEqualArgs&); /** bHasDynamicState が true の場合は必須。 * 量子化済みデータを、あるステートバッファから別のステートバッファへクローンする。 */ static void CloneDynamicState(FNetSerializationContext&, const FNetCloneDynamicStateArgs&); /** bHasDynamicState が true の場合は必須。 * ステートバッファ内の動的確保を解放し、データを既定ステートへリセットする。 */ static void FreeDynamicState(FNetSerializationContext&, const FNetFreeDynamicStateArgs&); }; //シリアライザとして実体化 UE_NET_IMPLEMENT_SERIALIZER(FExampleNetSerializer); //Irisに登録 UE_NET_IMPLEMENT_FORWARDING_NETSERIALIZER_AND_REGISTRY_DELEGATES(ExampleNetSeri alizer, FExampleNetSerializer)

20.

struct FExampleNetSerializer { /** 必須 Version番号 */ static constexpr uint32 Version = 0; // 以下の各トレイトは任意。下記の既定値と異なる場合にのみ指定すればよい。 /* 少なくとも 1 つの他のシリアライザへ呼び出しを転送する意図があり、 実装可能なすべての関数を確実に実装したい場合に指定する。 */ static constexpr bool bIsForwardingSerializer = false; /* 接続固有のシリアライズが必要な場合に指定する。使用は避けること! */ static constexpr bool bHasConnectionSpecificSerialization = false; /* CollectNetReferences の実装が必要な場合に指定する。*/ static constexpr bool bHasCustomNetReference = false; /* シリアライザが動的ステートを必要とする場合に指定する。 * CloneDynamicState と FreeDynamicState の実装が必須になる。*/ static constexpr bool bHasDynamicState = false; /** 同値デルタ圧縮方式が望ましくない場合にfalse を設定する。 * 例えば、シリアライザがそのステートに対して1 ビットしか書き込まない場合など。*/ static constexpr bool bUseDefaultDelta = true; /** 必須 外部ステートのサイズとアライメントを算出し、一部の関数の既定実装を 提供する */ typedef FSomeSourceType SourceType; /** QuantizedType の typedef は任意。ただしSourceType が POD でない場合は必須。 * 指定しない場合はSourceType とみなされる。QuantizedType は POD である必要がある。 */ typedef FSomePodType QuantizedType; /** 必須 ConfigType の typedef は必須。 */ typedef FNetSerializerConfig ConfigType; /** ほぼ必須 DefaultConfig は任意だが強く推奨される。*/ inline static const ConfigType DefaultConfig; /** 必須。Serialize は、シリアライゼーションコンテキストから提供される * ビットストリームへ量子化済みデータを書き込む責務を持つ。*/ static void Serialize(FNetSerializationContext&, const FNetSerializeArgs&); /** 必須。Deserialize は、シリアライゼーションコンテキストから提供される * ビットストリームから量子化済みデータを読み取る責務を持つ。*/ static void Deserialize(FNetSerializationContext&, const FNetDeserializeArgs&); /** 任意。Serialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。*/ static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&); /** 任意。Deserialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。 */ static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&); /** 任意。 ソースデータを、高速にシリアライズできるPOD 形式へ変換する。 * 量子化はオブジェクトごと・フレームごとに高々1 回しか実行されないため、 * シリアライズを高速化する目的でやや重い計算を行うのに絶好の機会である。 */ static void Quantize(FNetSerializationContext&, const FNetQuantizeArgs&); /** 任意。ただし QuantizedType の typedef または Quantize の実装がある場合は必須。 * dequantize 関数は、量子化されたステートを妥当なソースデータ形式へ変換する責務を持つ。 * 変換結果は、量子化前の元のソースデータとおおむね等しくなる。 */ static void Dequantize(FNetSerializationContext&, const FNetDequantizeArgs&); /** 任意。あるデータが他のデータと等しいかどうかを判定する。 * 既定の実装は等価演算子を使用する。*/ static bool IsEqual(FNetSerializationContext&, const FNetIsEqualArgs&); /** bHasDynamicState が true の場合は必須。 * 量子化済みデータを、あるステートバッファから別のステートバッファへクローンする。 */ static void CloneDynamicState(FNetSerializationContext&, const FNetCloneDynamicStateArgs&); /** bHasDynamicState が true の場合は必須。 * ステートバッファ内の動的確保を解放し、データを既定ステートへリセットする。 */ static void FreeDynamicState(FNetSerializationContext&, const FNetFreeDynamicStateArgs&); }; //シリアライザとして実体化 UE_NET_IMPLEMENT_SERIALIZER(FExampleNetSerializer); //Irisに登録 UE_NET_IMPLEMENT_FORWARDING_NETSERIALIZER_AND_REGISTRY_DELEGATES(ExampleNetSeri alizer, FExampleNetSerializer)

21.

struct FExampleNetSerializer { /** 必須 Version番号 */ static constexpr uint32 Version = 0; // 以下の各トレイトは任意。下記の既定値と異なる場合にのみ指定すればよい。 /* 少なくとも 1 つの他のシリアライザへ呼び出しを転送する意図があり、 実装可能なすべての関数を確実に実装したい場合に指定する。 */ static constexpr bool bIsForwardingSerializer = false; /* 接続固有のシリアライズが必要な場合に指定する。使用は避けること! */ static constexpr bool bHasConnectionSpecificSerialization = false; /* CollectNetReferences の実装が必要な場合に指定する。*/ static constexpr bool bHasCustomNetReference = false; /* シリアライザが動的ステートを必要とする場合に指定する。 * CloneDynamicState と FreeDynamicState の実装が必須になる。*/ static constexpr bool bHasDynamicState = false; /** 同値デルタ圧縮方式が望ましくない場合にfalse を設定する。 * 例えば、シリアライザがそのステートに対して1 ビットしか書き込まない場合など。*/ static constexpr bool bUseDefaultDelta = true; /** 必須 外部ステートのサイズとアライメントを算出し、一部の関数の既定実装を 提供する */ typedef FSomeSourceType SourceType; /** QuantizedType の typedef は任意。ただしSourceType が POD でない場合は必須。 * 指定しない場合はSourceType とみなされる。QuantizedType は POD である必要がある。 */ typedef FSomePodType QuantizedType; /** 必須 ConfigType の typedef は必須。 */ typedef FNetSerializerConfig ConfigType; /** ほぼ必須 DefaultConfig は任意だが強く推奨される。*/ inline static const ConfigType DefaultConfig; /** 必須。Serialize は、シリアライゼーションコンテキストから提供される * ビットストリームへ量子化済みデータを書き込む責務を持つ。*/ static void Serialize(FNetSerializationContext&, const FNetSerializeArgs&); /** 必須。Deserialize は、シリアライゼーションコンテキストから提供される * ビットストリームから量子化済みデータを読み取る責務を持つ。*/ static void Deserialize(FNetSerializationContext&, const FNetDeserializeArgs&); /** 任意。Serialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。*/ static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&); /** 任意。Deserialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。 */ static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&); /** 任意。 ソースデータを、高速にシリアライズできるPOD 形式へ変換する。 * 量子化はオブジェクトごと・フレームごとに高々1 回しか実行されないため、 * シリアライズを高速化する目的でやや重い計算を行うのに絶好の機会である。 */ static void Quantize(FNetSerializationContext&, const FNetQuantizeArgs&); /** 任意。ただし QuantizedType の typedef または Quantize の実装がある場合は必須。 * dequantize 関数は、量子化されたステートを妥当なソースデータ形式へ変換する責務を持つ。 * 変換結果は、量子化前の元のソースデータとおおむね等しくなる。 */ static void Dequantize(FNetSerializationContext&, const FNetDequantizeArgs&); /** 任意。あるデータが他のデータと等しいかどうかを判定する。 * 既定の実装は等価演算子を使用する。*/ static bool IsEqual(FNetSerializationContext&, const FNetIsEqualArgs&); /** bHasDynamicState が true の場合は必須。 * 量子化済みデータを、あるステートバッファから別のステートバッファへクローンする。 */ static void CloneDynamicState(FNetSerializationContext&, const FNetCloneDynamicStateArgs&); /** bHasDynamicState が true の場合は必須。 * ステートバッファ内の動的確保を解放し、データを既定ステートへリセットする。 */ static void FreeDynamicState(FNetSerializationContext&, const FNetFreeDynamicStateArgs&); }; //シリアライザとして実体化 UE_NET_IMPLEMENT_SERIALIZER(FExampleNetSerializer); //Irisに登録 UE_NET_IMPLEMENT_FORWARDING_NETSERIALIZER_AND_REGISTRY_DELEGATES(ExampleNetSeri alizer, FExampleNetSerializer)

22.

struct FExampleNetSerializer { /** 必須 Version番号 */ static constexpr uint32 Version = 0; // 以下の各トレイトは任意。下記の既定値と異なる場合にのみ指定すればよい。 /* 少なくとも 1 つの他のシリアライザへ呼び出しを転送する意図があり、 実装可能なすべての関数を確実に実装したい場合に指定する。 */ static constexpr bool bIsForwardingSerializer = false; /* 接続固有のシリアライズが必要な場合に指定する。使用は避けること! */ static constexpr bool bHasConnectionSpecificSerialization = false; /* CollectNetReferences の実装が必要な場合に指定する。*/ static constexpr bool bHasCustomNetReference = false; /* シリアライザが動的ステートを必要とする場合に指定する。 * CloneDynamicState と FreeDynamicState の実装が必須になる。*/ static constexpr bool bHasDynamicState = false; /** 同値デルタ圧縮方式が望ましくない場合にfalse を設定する。 * 例えば、シリアライザがそのステートに対して1 ビットしか書き込まない場合など。*/ static constexpr bool bUseDefaultDelta = true; /** 必須 外部ステートのサイズとアライメントを算出し、一部の関数の既定実装を 提供する */ typedef FSomeSourceType SourceType; /** QuantizedType の typedef は任意。ただしSourceType が POD でない場合は必須。 * 指定しない場合はSourceType とみなされる。QuantizedType は POD である必要がある。 */ typedef FSomePodType QuantizedType; /** 必須 ConfigType の typedef は必須。 */ typedef FNetSerializerConfig ConfigType; /** ほぼ必須 DefaultConfig は任意だが強く推奨される。*/ inline static const ConfigType DefaultConfig; /** 必須。Serialize は、シリアライゼーションコンテキストから提供される * ビットストリームへ量子化済みデータを書き込む責務を持つ。*/ static void Serialize(FNetSerializationContext&, const FNetSerializeArgs&); /** 必須。Deserialize は、シリアライゼーションコンテキストから提供される * ビットストリームから量子化済みデータを読み取る責務を持つ。*/ static void Deserialize(FNetSerializationContext&, const FNetDeserializeArgs&); /** 任意。Serialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。*/ static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&); /** 任意。Deserialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。 */ static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&); /** 任意。 ソースデータを、高速にシリアライズできるPOD 形式へ変換する。 * 量子化はオブジェクトごと・フレームごとに高々1 回しか実行されないため、 * シリアライズを高速化する目的でやや重い計算を行うのに絶好の機会である。 */ static void Quantize(FNetSerializationContext&, const FNetQuantizeArgs&); /** 任意。ただし QuantizedType の typedef または Quantize の実装がある場合は必須。 * dequantize 関数は、量子化されたステートを妥当なソースデータ形式へ変換する責務を持つ。 * 変換結果は、量子化前の元のソースデータとおおむね等しくなる。 */ static void Dequantize(FNetSerializationContext&, const FNetDequantizeArgs&); /** 任意。あるデータが他のデータと等しいかどうかを判定する。 * 既定の実装は等価演算子を使用する。*/ static bool IsEqual(FNetSerializationContext&, const FNetIsEqualArgs&); /** bHasDynamicState が true の場合は必須。 * 量子化済みデータを、あるステートバッファから別のステートバッファへクローンする。 */ static void CloneDynamicState(FNetSerializationContext&, const FNetCloneDynamicStateArgs&); /** bHasDynamicState が true の場合は必須。 * ステートバッファ内の動的確保を解放し、データを既定ステートへリセットする。 */ static void FreeDynamicState(FNetSerializationContext&, const FNetFreeDynamicStateArgs&); }; //シリアライザとして実体化 UE_NET_IMPLEMENT_SERIALIZER(FExampleNetSerializer); //Irisに登録 UE_NET_IMPLEMENT_FORWARDING_NETSERIALIZER_AND_REGISTRY_DELEGATES(ExampleNetSeri alizer, FExampleNetSerializer)

23.

struct FExampleNetSerializer { /** 必須 Version番号 */ static constexpr uint32 Version = 0; // 以下の各トレイトは任意。下記の既定値と異なる場合にのみ指定すればよい。 /* 少なくとも 1 つの他のシリアライザへ呼び出しを転送する意図があり、 実装可能なすべての関数を確実に実装したい場合に指定する。 */ static constexpr bool bIsForwardingSerializer = false; /* 接続固有のシリアライズが必要な場合に指定する。使用は避けること! */ static constexpr bool bHasConnectionSpecificSerialization = false; /* CollectNetReferences の実装が必要な場合に指定する。*/ static constexpr bool bHasCustomNetReference = false; /* シリアライザが動的ステートを必要とする場合に指定する。 * CloneDynamicState と FreeDynamicState の実装が必須になる。*/ static constexpr bool bHasDynamicState = false; /** 同値デルタ圧縮方式が望ましくない場合にfalse を設定する。 * 例えば、シリアライザがそのステートに対して1 ビットしか書き込まない場合など。*/ static constexpr bool bUseDefaultDelta = true; /** 必須 外部ステートのサイズとアライメントを算出し、一部の関数の既定実装を 提供する */ typedef FSomeSourceType SourceType; /** QuantizedType の typedef は任意。ただしSourceType が POD でない場合は必須。 * 指定しない場合はSourceType とみなされる。QuantizedType は POD である必要がある。 */ typedef FSomePodType QuantizedType; /** 必須 ConfigType の typedef は必須。 */ typedef FNetSerializerConfig ConfigType; /** ほぼ必須 DefaultConfig は任意だが強く推奨される。*/ inline static const ConfigType DefaultConfig; /** 必須。Serialize は、シリアライゼーションコンテキストから提供される * ビットストリームへ量子化済みデータを書き込む責務を持つ。*/ static void Serialize(FNetSerializationContext&, const FNetSerializeArgs&); /** 必須。Deserialize は、シリアライゼーションコンテキストから提供される * ビットストリームから量子化済みデータを読み取る責務を持つ。*/ static void Deserialize(FNetSerializationContext&, const FNetDeserializeArgs&); /** 任意。Serialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。*/ static void SerializeDelta(FNetSerializationContext&, const FNetSerializeDeltaArgs&); /** 任意。Deserialize と同じだが、ビットパッキングのためにACK 済みの前ステートが * 提供される。既定では同値最適化を行う実装が提供される( 1 ビットのコストがかかる)。 */ static void DeserializeDelta(FNetSerializationContext&, const FNetDeserializeDeltaArgs&); /** 任意。 ソースデータを、高速にシリアライズできるPOD 形式へ変換する。 * 量子化はオブジェクトごと・フレームごとに高々1 回しか実行されないため、 * シリアライズを高速化する目的でやや重い計算を行うのに絶好の機会である。 */ static void Quantize(FNetSerializationContext&, const FNetQuantizeArgs&); /** 任意。ただし QuantizedType の typedef または Quantize の実装がある場合は必須。 * dequantize 関数は、量子化されたステートを妥当なソースデータ形式へ変換する責務を持つ。 * 変換結果は、量子化前の元のソースデータとおおむね等しくなる。 */ static void Dequantize(FNetSerializationContext&, const FNetDequantizeArgs&); /** 任意。あるデータが他のデータと等しいかどうかを判定する。 * 既定の実装は等価演算子を使用する。*/ static bool IsEqual(FNetSerializationContext&, const FNetIsEqualArgs&); /** bHasDynamicState が true の場合は必須。 * 量子化済みデータを、あるステートバッファから別のステートバッファへクローンする。 */ static void CloneDynamicState(FNetSerializationContext&, const FNetCloneDynamicStateArgs&); /** bHasDynamicState が true の場合は必須。 * ステートバッファ内の動的確保を解放し、データを既定ステートへリセットする。 */ static void FreeDynamicState(FNetSerializationContext&, const FNetFreeDynamicStateArgs&); }; //シリアライザとして実体化 UE_NET_IMPLEMENT_SERIALIZER(FExampleNetSerializer); //Irisに登録 UE_NET_IMPLEMENT_FORWARDING_NETSERIALIZER_AND_REGISTRY_DELEGATES(ExampleNetSeri alizer, FExampleNetSerializer)

24.

量子化 元データをPODに変換する処理 クラスごとの特殊な実装を加えることが可能で 例えば浮動小数点値を整数型に変換する実装などがある (FVector_NetQuantizeなど)

25.

デルタ圧縮 シリアライズのタイミングでビットレベルのデルタ圧縮がかかる ● 二つのデルタ圧縮 ○ Same-value デルタ圧縮 ■ 値が変化していない場合に1bitに圧縮 ○ ベースライン デルタ圧縮 ■ NetSerializerにSerializeDelta/DeserializeDeltaがが実装されている型が対象 ■ 定期的に更新されるベースラインからのビットレベル差分でデータが送信される ● net.Iris.MinimumNumberOfFramesBetweenBaselines(デフォルト値60) [/Script/IrisCore.ObjectReplicationBridgeConfig] ; Pawn派生クラスはエンジンの定義で有効化されている ; 指定したクラスの派生クラスにも適用される +DeltaCompressionConfigs=(ClassName=/Script/MyGame.MyActor) ; 継承で有効になったクラスを個別に無効化したい場合 +DeltaCompressionConfigs=(ClassName=/Script/MyGame.MyLightweightPawn, bEnableDeltaCompression=false)

26.

量子化やデルタ圧縮が実装されている型 量子化 デルタ FVector ✕ ✕ 完全精度 (変更時196bit送信) FVector_NetQuantize 〇 〇 整数キャスト FVector_NetQuantize10 〇 〇 3bit固定小数点 FVector_NetQuantize100 〇 〇 7bit固定小数点 FRotator 〇 〇 各要素16bitに FQuat 〇 ✕ wを省略&復元 int32/int64/uint32/uint64 ✕ 〇 float/double ✕ 〇 FTransform ✕ ✕ 構造体 : FQuat + FVector + FVector に分解 FMatrix ✕ ✕ 構造体 : ( FPlane + Double ) x 4 に分解 (1012bits) 型

27.

FastArraySerializer FastArraySerializerを使ったクラスはIrisFastArraySerializerに基底クラスを置換する コードサンプルはLyra等のソース内を ● ● ● FastArrayは要素のPush(MarkItemDirty)に対応しているので 全要素に対する比較を行わなくて良くなる 最終的に生成されるシリアライズ済みデータは同等 インデックス付変更コールバックを利用可能 ○ PreReplicatedRemove ○ PostReplicatedAdd/Change/Receive ○ RepNotifyは配列全体の変更しか検出できない

28.

配列ののChangeMask TArray型配列やFastArraySerializerの配列は 63bitのChangeMaskを持ち部分更新を行う 配列長64要素を超えるときモジュロ演算 (Index % 63)で対応ビットを選択しマスクを共有する Index 0に書き込み 注意! 検証のためデルタ圧縮未適用 ● 配列長64の配列に対する部分書き込みの例 ○ Index 0 または 63 に書き込み ■ 0 と 63 の二つが送信 ○ Index 1~62 に書き込み ■ 1つだけ送信 デルタ圧縮でまき込まれる要素を圧縮できると良い Index 1に書き込み

29.

FastArrayとDelta圧縮のパフォーマンステスト 256x256x4byte(256KB)の テクスチャをレプリケートするテスト タイルで分割 (16pixelx16pixel) 256タイルの配列で制御 ● ● ● TArray+デルタ圧縮なし IrisFastArray+デルタ圧縮なし IrisFastArray+デルタ圧縮あり

30.

FastArrayとDelta圧縮のパフォーマンス TArray配列でPushなしだと 配列全体をなめるプーリングが毎Tick処理されてしまう

31.

FastArrayとDelta圧縮のパフォーマンス TArray FastArray デルタ圧縮なし FastArray デルタ圧縮あり 1タイル書き込みで4タイル巻き込まれる例 デルタ圧縮で巻き込まれタイルが 8740bit から 516bitに圧縮されている

32.

Filter アクターの情報をクライアントに送るべきどうかの関連性(Relevancy)を制御する仕組み Static Exculusion Dynamic Owner NetObjectFilter Inclusion

33.

Filter : NetObjectFilter 空間フィルタなどの動的なフィルタ クラスごとに一つのNetObjectFilterに属することができる 組み込みのNetObjectGridWorldLocFilterは ReplicationGraphの2DGrid相当の機能を持つ 適切な設定がおこなわれているかは以下のコンソールコマンドで確認する Net.Iris.PrintDynamicFilterClassConfig Net.Iris.PrintDynamicFilterClassConfigIssues [/Script/IrisCore.NetObjectFilterDefinitions] +NetObjectFilterDefinitions=(FilterName=Spatial, ClassName=/Script/IrisCore.NetObjectGridWorldLocFilter, ConfigClassName=/Script/IrisCore.NetObjectGridFilterConfig) +NetObjectFilterDefinitions=(FilterName=NotRouted, ClassName=/Script/IrisCore.FilterOutNetObjectFilter, ConfigClassName=/Script/IrisCore.FilterOutNetObjectFilterConfig) DefaultSpatialFilterName=Spatial ;+FilterConfigs=(ClassName=/Script/Engine.LevelScriptActor, DynamicFilterName=NotRouted) ; NotRouted means that this type will not be replicated [/Script/IrisCore.NetObjectGridFilterConfig] CellSizeX=10000.0 CellSizeY=10000.0

34.

Filter : Exclusion と Inclusion Exclusion:特定のグループに属したアクターを接続先ごとに通信から除外する Inclusion:特定のグループに属しているObjectフィルタで除外されたアクターに対して 接続単位で除外を取り消すことができる ● ● ● グループはInclusionFilterGroupかExclusionFilterGroupのどちらかに設定されている アクターを1つまたは複数のグループに属させる 各グループに対して接続先ごとに許可を不許可を設定できる

35.

Filter : Exclusion と Inclusion NetObjectFilter To ConnectionA To ConnectionB To ConnectionC Group Spectator : Exculution Group Wolves : Inclusion Disallow Disallow Disallow Arrow Allow Disallow

36.

Prioritizer クラスごとにプライオリタイザを割り当ててアクター毎の送信優先度を制御 ● ● ● ● ● ● 優先度でソートして送信 ○ 先送りになった複製対象は優先度を蓄積 (そのうち送信される) 蓄積した値が1未満は送信対象から除外。送信間隔制御として機能する ○ 設定値0.2なら、5tickに1回の送信チャンス レガシーではアクター毎のNetPriorityをベースに距離などの係数を乗算していたがIrisは即値 ini内で NetObjectPrioritizerDefinitionsの配列のうち 有効な一番最初の定義がデフォルト設定になる AlwaysRelevantやOnlyRelevantToOwnerが設定されたアクターは 静的プライオリティとして制御されてアクターのNetPriorityを参照する 手動で静的プライオリティをあたえる UE::Net::FReplicationSystemUtil::SetStaticPriority(ThisActor, 1.0f);

37.

Prioritizer デフォルトで距離ベースのPrioritizerが設定されています NetObjectPrioritizerDefinitionsの 一つ目がデフォルト [/Script/IrisCore.NetObjectPrioritizerDefinitions] +NetObjectPrioritizerDefinitions=(PrioritizerName=DefaultPrioritizer, ClassName=/Script/IrisCore.SphereNetObjectPrioritizer, ConfigClassName=/Script/IrisCore.SphereNetObjectPrioritizerConfig) +NetObjectPrioritizerDefinitions=(PrioritizerName=PlayerStatePrioritizer, ClassName=/Script/IrisCore.NetObjectCountLimiter, ConfigClassName=/Script/Engine.PlayerStateCountLimiterConfig) ; PrioritizerConfigs ; PlayerStateは位置情報を持たないのでPlayerStatePrioritizerを割り当てる NetPriorityは無視されることに注意 +PrioritizerConfigs=(ClassName=/Script/Engine.PlayerState, PrioritizerName=PlayerStatePrioritizer, bForceEnableOnAllInstances=true) [/Script/IrisCore.SphereNetObjectPrioritizerConfig] ; エンジン既定: InnerRadius=2000, OuterRadius=10000 InnerRadius=5000.0 OuterRadius=30000.0 InnerPriority=1.0 OuterPriority=0.2 OutsidePriority=0.1 プライオリタイザーの パラメータオーバーライド bForceEnableOnAllInstanceは AlwaysRelevantでもPrioritizerを有効化するフラグ

38.
[beta]
ポーリングとPushModel

プッシュモデルはポーリング処理負荷を軽減する
特にプロパティ書き換えタイミングが
限定されたアクターに対して有効

void AActor::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
FDoRepLifetimeParams SharedParams;
SharedParams.bIsPushBased = true;
DOREPLIFETIME_WITH_PARAMS_FAST(AActor, bReplicateMovement, SharedParams);
…
}
void AActor::SetReplicatingMovement(bool bInReplicateMovement)
{
bReplicateMovement = bInReplicateMovement;
MARK_PROPERTY_DIRTY_FROM_NAME(AActor, bReplicateMovement, this);
}

void AGameStateBase::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps) const
{
FDoRepLifetimeParams Params;
Params.bIsPushBased = true;
//注意 DOREPLIFETIME は PushBased = falseに展開されてしまう!
DOREPLIFETIME( AGameStateBase, bReplicatedHasBegunPlay );
}

39.

PushModel動作確認 Net.Iris.PrintPushBasedStatuses LogIris: Display: ################ Start Printing Push-Based Statuses ################ LogIris: Display: LogIris: Display: MoverNetworkPredictionLiaisonComponent (RefCount: 4) (PushBased: 0) LogIris: Display: Printing properties that aren't push-based: LogIris: Display: /Script/NetworkPrediction.NetworkPredictionComponent:NetworkPredictionProxy LogIris: Display: /Script/NetworkPrediction.NetworkPredictionComponent:ReplicationProxy_Auto(略)LogIris: Display: /Script/NetworkPrediction.NetworkPredictionComponent:ReplicationProxy_Simulated LogIris: Display: /Script/NetworkPrediction.NetworkPredictionComponent:ReplicationProxy_Replay LogIris: Display: GameStateBase (RefCount: 1) (PushBased: 0) LogIris: Display: Printing properties that aren't push-based: LogIris: Display: /Script/Engine.GameStateBase:SpectatorClass LogIris: Display: /Script/Engine.GameStateBase:GameModeClass LogIris: Display: /Script/Engine.GameStateBase:ReplicatedWorldTimeSecondsDouble LogIris: Display: /Script/Engine.GameStateBase:bReplicatedHasBegunPlay LogIris: Display: NetCanvasActor (RefCount: 1) (PushBased: 0) LogIris: Display: Printing properties that aren't push-based: LogIris: Display: /Script/MultiIris.NetCanvasActor:Tiles LogIris: Display: GameplayDebuggerCategoryReplicator (RefCount: 4) (PushBased: 1) LogIris: Display: MultiIrisPlayerController (RefCount: 4) (PushBased: 1) LogIris: Display: MultiMoverPawn (RefCount: 4) (PushBased: 1) LogIris: Display: NetCanvasPainterComponent (RefCount: 4) (PushBased: 1) LogIris: Display: PlayerState (RefCount: 4) (PushBased: 1) LogIris: Display: NetworkPredictionReplicatedManager (RefCount: 1) (PushBased: 1) LogIris: Display: WorldDataLayers (RefCount: 1) (PushBased: 1) LogIris: Display: WorldSettings (RefCount: 1) (PushBased: 1) LogIris: Display: LogIris: Display: ################ Stop Printing Push-Based Statuses ################

40.

ListenServer レガシーレプリケーションでは、各接続先に対する処理負荷が高かっ たためListenServer動作時に1Tickに処理できるクライアント数に制限 を設けていた IrisではNetClientTicksPerSecond設定値による送信先クライアント数 上限はなく毎Tick全クライアントにパケットを送信する

41.

並列化 [/Script/Engine.Engine] +IrisNetDriverConfigs=(NetDriverName=GameNetDriver, bCanUseIris=true, bCanUseParallelNetConnectionTick=true) [SystemSettings] net.iris.AllowParallelNetTick=1 [/Script/OnlineSubsystemUtils.IpNetDriver] ReplicationSystemConfigServer=(bAllowParallelTasks=true, MinConnectionsForParallelTick=2)

42.

互換性 ● そのまま動く ○ UPROPERTYのレプリケーション ○ RepNotify ■ net.Iris.UsePrevReceivedStateForOnReps でクライアント側比較の有無を制御 ○ PushModel (MARK_PROPERTY_DIRTY) ● 気を付ける ○ RPC ■ 処理順序やタイミングが変化する可能性 ■ net.Iris.ExecuteReliableRPCsBeforeApplyState でレプリケートの前か後かを選択 ○ NetSerialize > NetSerializer ○ FastArraySerializer > FIrisFastArraySerializerに置換 ○ ReplicationGraphはフィルタやプライオリタイザーに移行する ○ Replayはレガシーレプリケーションでしか処理できない

43.

有効化 Irisはエンジンにモジュールとして存在するので、 Irisを使う各モジュールの*.build.csでSetupIrisSupport()を呼び出す [SystemSettings] net.Iris.UseIrisReplication=1 net.SubObjects.DefaultUseSubObjectReplicationList=1

44.

アップデート履歴 Iris導入 IrisNetConfig既定化 5.2 5.1 Production-Ready 5.7 5.3 bUseIris=true デフォルト 5.8 Beta Runtime/Net/Irisに移動 bUseIris削除 SeamlessTravel対応

45.

資料 Developing and Launching a New Replication System | Unreal Fest Orlando 2025 次世代ネットワーク通信実装Irisについて @docswell

46.

TIPS “-NewConsole” : DedicatedServerやヘッドレスクライアントのテスト時に コマンドラインオプションに追加すると コンソールコマンド入力欄つきログウィンドウを出せる

47.

第 II 部 Mover 次世代移動コンポーネントのネットワーク対応について

48.

Moverとは 特定のクラスに依存しない モジュラー構造 物理をつかった遊びに対応 操作対象のアクターやコンポーネ ントに制限がない モジュールを組み合わせて構成 拡張性に配慮 物理ベースの移動や 剛体とのインタラクション 同期の仕組みを選択式に マルチスレッドに対応(中) CMCユーザーフレンドリー オフラインゲームで複雑な同期 コードを省略できる 一部処理をワーカスレッドにオフ ロードできるように 基本的なインターフェイスは (大体)同じものがそろっている 物理同期

49.

Moverのモジュール構造 CMC (CharacterMovementComponent) MoverComponent 歴史ある UBaseMovementMode キャラクター移動コンポーネント 歩く・落ちる・泳ぐ — 移動の計算 ● ● ● EMovementMode クラス継承による拡張 組み込み ○ ルートモーション ○ 移動同期 LayeredMove MovementModifier Backend Liaison バックエンドモジュール

50.

Backend Liaison 3つのバックエンドから一つを選択 ● ● ● Standalone Liaison ○ ネットワーク同期が不要な場合 ○ 一部の処理をワーカースレッドに分散可能 NetworkPrediction Liaison ○ CMCのネットワーク同期システムに近い動作 ○ NetworkPredictionPluginの仕組みを利用 ChaosMover Liaison ○ NetworkPhysicsLiaisonから改良&移行 ○ 物理シミュレーションベースで動作しているタイトル用 ○ 物理界全体のロールバックや再シミュレーション

51.

NetworkPredictionPlugin(NPP) NetworkPredictionManager NPP Mover 同期処理を実現するプラグイン ゲームワールドとは直接繋がっていない Mover動作時に強制リンクされる ● ● ● ● 同期エラー検出 対象アクターの過去フレームの ステートの管理 ロールバック時の再シミュレーション ServerRPC送信 NetworkPredictionLiaison MovorCompomonent Title 詳しくは Plugins/Runtime/NetworkPrediction/readme.txt を参照 Actor

52.

NPPの同期制御比較

53.

補正命令を Autonomousに返す 入力と移動結果 を送信 パケット毎に Tickを行う サーバー側でエ ラー検出

54.

エラー検出が AutonomousClient に移動 入力のみ送信 Tick時にバッファ された入力を取得 ForwardPredict用に Inputも送信

55.

CMCとMover+NPPの比較 CMC Mover+NPP クライアントからの 送信データフォーマット 入力 + 移動結果 Combineによる合成 入力のみ(多重化) 多重化によりパケロスに強く サーバーでの処理タイミング パケットを解析した直後 GameThread内で バッファされたデータを参照 CMCはクライアントとサーバーの TickRateの差が大きいとサーバー 負荷が上昇する 検証 サーバー上で サーバーの移動結果とクライアント からの移動結果を比較 クライアント上で ステート(結果)を比較 検証負荷がクライアントへ 補正 サーバーからの結果を適用 ロールバックして 再シミュレーション SimulateProxyの動作 スムーシング スムーシング or 前方予測

56.

NetworkPrediction設定 ● ● ● ● ● ● ● PreferredTickingPolicy : ○ Fixed / Independent FixedTickFrameRate ○ Fixedの場合のシミュレーションフレームレート SimulatedProxy Network LOD ○ SimulateProxyの動作 ○ Interpolated(単純補間) / ForwardPredict(前方予測) Enable Fixed Tick Smoothing (Fixed/Independent)TickInterpolation Buffer MS ○ Interpolated時の遅延位置 (Fixed/Independent)TickInputSendCount ○ Inputの多重化数 - パケロス耐性 MaximumRemoteInputFaultLimit ○ Fault(通信途絶)からの復帰判定の入力数閾値 途絶後、バッファがたまるまで前回のキーを使用する ○ InputSendCountがデフォルトで6なので、パケットが 到達したら常にこの条件を満たしてしまう

57.

TickingPolicy Fixed Independent 備考 Tickのデルタタイム 共通固定デルタタイム ゲームのデルタタイム ゲームのフレームレートはどちらも自 由に設定してよい 入力のバッファリング あり (遅延として現れる) なし 遅延なしで適用 出来る限り全クライアントの入力があ る状態でTickしたいのでバッファリン グがある TimeDilation調整 あり 不要 バッファの余裕に応じてクライアント のTickRateを調整する SimulateProxyNetworkLOD Interpolated | ForwardPredict Interporated 現時点でSimulateProxyは Interpolated推奨 (公式ドキュメントより) ロールバック グループロールバック 単体ロールバック (すべての ForwardPredictアクター ) RootMotion AnimRootMotion(単発) AnimRootMotion(単発) RootMotionAttribute(ABP出力) Tickタイミング マネージャから呼び出される 通常のコンポーネントのTick

58.

Enable Fixed Tick Smoothing Fixedモードでは、シミュレーションTick時間と表示 Tick時間にズレが生じる ズレた場合にシミュレーションフレーム間の補間を入 れる機能 操作しているキャラクターは前方予測 (ForwardPredict)で動作 np.Smoothing.Disable 1 で一時的に無効化できる 補間はスムーシング対象のMeshComponentに対して 適用されるので、カメラをメッシュの子にアタッチす る必要があることに注意 Fixed Tick Frame Rate : 10

59.

TickingPolicy : Fixed のタイムライン 表示Tick Fixed Tick Fixed Step Smoothing参照 1固定デルタ分 後方を参照して表示 Fixed Step Fixed Step Fixed Step Fixed Step Fixed Step Fixed Step Fixed Step Fixed Step Fixed Step

60.

Moverアップデート履歴 StandaloneLiaisonの非同期動作 PhysicsMover > ChaosMover RootMotionベースの移動 任意平面での移動サポート Mover導入 Experimental 5.5 5.4 NavWalking BaseMovement改善 TimeDilation制御 5.6 MovementModifier 5.9~ 6.0~ 5.7 5.8 ブループリント対応の強化 アニムモンタージュ同期 グループムーブメント 脱Experimentalへ

61.

資料 Network Prediction: Replication's Crystal Ball | Unreal Fest Bali 2025 Project Titanから学ぶオープンワールドワークフローと次世代 移動コンポーネントMoverの設計 @docswell

62.

まとめ IrisがUE5.8でプロダクションレディに 特に大規模マルチプレイのパフォーマンスを改善します MoverはExperimentalステートのままですが アップデート毎に数多くの改善が盛り込まれ 脱Experimentalに向けて進行中

63.

Thank you