1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UE5 プロパティのレプリケーションについてのメモ

1
Last updated at Posted at 2026-02-19

概要

UE5マルチプレイ時のプロパティのレプリケーションについてのメモ書きです。

更新履歴

日付 内容
2026/02/19 初版

参考

以下を参考にさせて頂きました、ありがとうございます。
UE4でマルチプレイヤーゲームを作ろう【CEDEC 2019】
UE公式:プロパティのレプリケーション
UE公式:Replicated Subobject
UE公式:RPC について
UE4 MultiPlayer Online Deep Dive: 実践編1
[UE4] マルチプレイでの所有権とRPC
[UE4]ネットワーク対応をしたオブジェクトを作成
UE5 同期処理(レプリケーション)のC++実装
Understanding replication atomicity in Unreal Engine

環境

Windows11
Visual Studio 2022
UnrealEngine 5.6, 5.7

関連ソース

"Engine\Source\Runtime\CoreUObject\Public\UObject\Object.h"
"Engine\Source\Runtime\Engine\Classes\GameFramework\Actor.h"

レプリケーションについて

定義方法

基本的な使い方として、UPROPERTYメタ情報にReplicated を付ける。OnRep関数を使う場合はReplicatedUsing=OnRepFuncName とすると OnRepFuncName()を定義でき、プロパティが変更された場合に呼びだされます。

OnRep関数

レプリケーション対象の変数が変化したときにクライアントに対して実行されます、サーバー側で実行したい場合はサーバー側が明示的にOnRep関数を呼び出す必要があります。

RPC(Remote Procedure Calls)

UFUNCTIONメタ情報に Server Client NetMulticast のいずれかをつける。信頼性ありなしはReliable UnReliable でそれぞれ指定できる

各クラスでの実装方法について

ACharacterクラス(APawnクラス)でレプリケートする

実際のキャラクタークラスに処理を書く場合は以下のような感じになります。レプリケートとサーバーRPCの実行、OnRep関数も使う場合のテストコード。

AMyCharacter.h
UCLASS()
class SAMPLE_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

..省略..
public:
	// ネットワークレプリケーションに使用されるプロパティを返す
	virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;


	// レプリケートする値
	UPROPERTY(ReplicatedUsing=OnRep_TestValue)
	int32 TestValue = 0;

	// OnRep関数
	UFUNCTION()
	void OnRep_TestValue(const int32 _OldValue);

	// アクセサ
	int32 GetReplicated_TestValue() const { return(TestValue); }
	void SetReplicated_TestValue(int32 _InValue);

	// サーバーRPC
	UFUNCTION(Server, Reliable)
	void ServerRPC_TestValue(int32 _InValue);
};
AMyCharacter.cpp
#include "Net/UnrealNetwork.h"

void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, TestValue);
}

// OnRep関数
void AMyCharacter::OnRep_TestValue(const int32 _OldValue)
{
}

// Setter
void AMyCharacter::SetReplicated_TestValue(int32 _InValue)
{
	// Authorityならそのまま設定、AutonomousProxyの場合はServerRPCを呼ぶ
	if( HasAuthority() ){
		const auto _Old = TestValue;
		TestValue = _InValue;
		OnRep_TestValue(_Old);
		return;
	}
	check(GetLocalRole() == ROLE_AutonomousProxy);	// SimulatedやNoneの場合サーバRPCは送信不可
	ServerRPC_TestValue(_InValue);		// ServerRPC
}

// サーバーRPC
void AMyCharacter::ServerRPC_TestValue_Implementation(int32 _InValue)
{
	SetReplicated_TestValue(_InValue);
}

APlayerControllerクラスでレプリケートする

サーバー側は全員分のコントローラーを持っていますが、クライアント側は自分のコントローラーしか持っていないため他プレイヤーのレプリケートされた値を取得することができません。
一方、クライアント側のネットワークロールは AutonomousProxy となるため(APawnと一致する)サーバーRPCを実行することはできます。

APlayerStateクラスで レプリケートする

サーバー側もクライアント側も各プレイヤーに対応した APlayerStateクラスを持っているため他プレイヤーのレプリケートされた値を見ることができます。
一方、クライアント側のネットワークロールは SimulatedProxyのため(APawnと一致しない)サーバーRPCを実行することができません。

オーナーを APlayerControllerにすればサーバーRPCが使えるようになりそうですが試していません。

APlayerStateクラスとAPlayerControllerクラスの併用

レプリケートしたい値を APlayerStateに置きつつ、サーバーRPCを APlayerControllerに置くこともできます。以下コード例。

AMyPlayerState.h
UCLASS()
class SAMPLE00_API AMyPlayerState : public APlayerState
{
    GENERATED_BODY()

..省略..
public:
	virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;

	// レプリケートする値
	UPROPERTY(ReplicatedUsing=OnRep_TestValue)
	int32 TestValue = 0.0f;

	// OnRep関数
	UFUNCTION()
	void OnRep_TestValue(const int32 _Old);

	int32 GetReplicated_TestValue(){ return(TestValue); }
	void SetReplicated_TestValue(int32 _TestValue);
};

レプリケートしたい値のセッターにサーバーRPCを入れます。
ただし APlayerState では使えないため APlayerControllerにサーバーRPCを用意しそれを呼び出します。

AMyPlayerState.cpp
void AMyPlayerState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, TestValue);
}

void AMyPlayerState::OnRep_TestValue(const int32 _Old)
{
	UE_LOG(LogTemp, Log, TEXT("OnRep>> %d -> %d"), _Old, TestValue);
}

void AMyPlayerState::SetReplicated_TestValue(int32 _TestValue)
{
	// Authorityならそのまま設定、AutonomousProxyの場合はServerRPCを呼ぶ
	if( HasAuthority() ){
		const auto _Old = TestValue;
		TestValue = _TestValue;
		OnRep_TestValue(_Old);
		return;
	}
	
	if (auto _Pawn = GetPawn()) {
		if ( auto _PC = Cast<AMyPlayerController>(_Pawn->GetController()) ) {
			check(_PC->GetLocalRole() == ROLE_AutonomousProxy);
			_PC->ServerRPC_TestValue(_TestValue);	// ServerRPC
		}
	}
}

サーバーRPC用のメソッドを書きます。

AMyPlayerController.h
UCLASS()
class SAMPLE_API AMyPlayerController : public APlayerController
{
	GENERATED_BODY()

..省略..
public:
	// サーバーRPC
	UFUNCTION(Server, Reliable)
	void ServerRPC_TestValue(int32 _InValue);
};

APlayerStateにあるセッターを呼び出します。

AMyPlayerController.cpp
#include "Net/UnrealNetwork.h"

// サーバーRPC	##サーバープロセス上でのみ実行される
void AMyPlayerController::ServerRPC_TestValue_Implementation(int32 _TestValue)
{
	if( AMyPlayerState* _PS = GetPlayerState<AMyPlayerState>() ){
		_PS->SetReplicated_TestValue(_TestValue);
	}
}

これで APlayerState にある値をレプリケートもできてクライアント側からの変更もできて他プレイヤーからの参照もできます。

構造体でのレプリケーションについて

構造体で関連した値をまとめてレプリケートすることもできます。

構造体のレプリケート実装例

int型2つを持つ構造体を定義してこれをレプリケートするサンプルコードです。

TestStruct.h
// 構造体のレプリケーションテスト用
USTRUCT(BlueprintType)
struct FTestStruct
{
	GENERATED_BODY()

	FTestStruct() {}

	// 変数A
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int32	PropertyA = 0;

	// 変数B
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int32	PropertyB = 0;


	// 比較関数
	bool operator==(const FTestStruct& _Other) const
	{
		return((PropertyA == _Other.PropertyA) && (PropertyB == _Other.PropertyB));
	}

	bool operator!=(const FTestStruct& _Other) const
	{
		return !(*this == _Other);
	}
};

プロパティを持つキャラクタークラスは通常と変わりません。

AMyCharacter.h
UCLASS()
class AMyCharacter : ACharacter
{
..省略..

public:
	virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;

	// レプリケートする構造体
	UPROPERTY(ReplicatedUsing = OnRep_TestStruct)
	FTestStruct TestStruct;

	// OnRep
	UFUNCTION()
	void OnRep_TestStruct();

};

AMyCharacter.cpp
void AMyCharacterBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, TestStruct);
}

// OnRep
void AMyCharacterBase::OnRep_TestStruct()
{
	UE_LOG(LogTemp, Warning, TEXT("A:%d, B:%d"), TestStruct.PropertyA, TestStruct.PropertyB);
}

構造体をレプリケートする場合の注意点

構造体のレプリケートは通常変化した値だけ送信されます。
なので例えば PropertyAの変更を送ってからPropertyBの変更を送る場合、PropertyAがパケットロスで届かない&再送される前にPropertyBが届くケースがありえます。そのためクライアント側ではサーバー側ではありえない中途半端な値の状態になるケースになる可能性があるので注意が必要です。
これを回避するには構造体にシリアライザーを書いてまとめてしまうことで可能ですが、この場合毎回変更していない値も送られるためネットワーク帯域の使用効率が悪くなってしまいます。

Componentでのレプリケートについて

コンポーネントでのレプリケートは ACharacter とほとんど変わりませんが、Ownerのアクターがレプリケートされているのが必須で、他には OwnerによってRPCが使えるかどうかが影響します。

ActorComponentを使う

以下はFloatとInt変数をレプリケートするコンポーネントのサンプルコード。

ReplicatedStateComponent.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Net/UnrealNetwork.h"
#include "ReplicatedStateComponent.generated.h"

UCLASS()
class SAMPLE00_API UReplicatedStateComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	UReplicatedStateComponent();

	virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;


protected:
	// レプリケート対象1
	UPROPERTY(ReplicatedUsing=OnRep_ValFloat)
	float ValFloat = 0.0f;
	// レプリケート対象2
	UPROPERTY(Replicated)
	int32 ValInt = 0;

	// OnRep
	UFUNCTION()
	void OnRep_ValFloat();

public:
	// アクセサ
	float GetValFloat() const { return(ValFloat); }
	int32 GetValInt() const { return(ValInt); }

	// サーバーRPC
	UFUNCTION(Server, Reliable)
	void ServerRPC_ValFloat(float _InValue);

	// サーバーRPC
	UFUNCTION(Server, Reliable)
	void ServerRPC_ValInt(int32 _InValue);

};

ReplicatedStateComponent.cpp
#include "ReplicatedStateComponent.h"

UReplicatedStateComponent::UReplicatedStateComponent()
{
	// レプリケーション設定
	SetIsReplicatedByDefault(true);
}

void UReplicatedStateComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, ValFloat);
	DOREPLIFETIME(ThisClass, ValInt);
}

// OnRep
void UReplicatedStateComponent::OnRep_ValFloat()
{
	UE_LOG(LogTemp, Log, TEXT("%s : %f"), *FString(__FUNCTION__), ValFloat);
}

// サーバーRPC
void UReplicatedStateComponent::ServerRPC_ValFloat_Implementation(float _InValue)
{
	ValFloat = _InValue;
}

// サーバーRPC
void UReplicatedStateComponent::ServerRPC_ValInt_Implementation(int32 _InValue)
{
	ValInt = _InValue;
}

実装側キャラクタークラスにてコンポーネントを作成するだけでOKです。

MyCharacter.cpp
// ヘッダファイルに以下のプロパティ定義をする
//UPROPERTY()
//UReplicatedStateComponent* StateComponent;

AMyCharacter::AMyCharacter()
{
	// レプリケート管理コンポーネントを作成
	StateComponent = CreateDefaultSubobject<UReplicatedStateComponent>(TEXT("StateComponent0"));
}

UObjectのレプリケートについて

AddReplicatedSubObject を使う

UE5.1 から追加されたらしい AddReplicatedSubObject を使ってみます。
UObject を継承したレプリケートする変数を持つオブジェクトを定義します。
RPCを使う場合は int32 GetFunctionCallspace()bool CallRemoteFunction() が必要になります。
以下サンプルコード。

ReplicatedSubObject.h
#pragma once

#include "CoreMinimal.h"
#include "Logging/LogMacros.h"
#include "ReplicatedSubObject.generated.h"

UCLASS()
class UMySubObject : public UObject
{
	GENERATED_BODY()

public:
	virtual bool IsSupportedForNetworking() const override { return true; }

	// UObject RPC を Outer Actor にルーティング		##RPCを使用しないなら不要
	virtual int32 GetFunctionCallspace(UFunction* Function, FFrame* Stack) override;
	// NetDriverのProcessRemoteFunctionを呼び出し		##RPCを使用しないなら不要
	virtual bool CallRemoteFunction(UFunction* Function, void* Parms, FOutParmRec* OutParms, FFrame* Stack) override;
	
	void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const override;

public:
	UPROPERTY(ReplicatedUsing = OnRep_Value)
	int32 Value = 0;

	void SetRelicated_Value(int32 _InValue);

	UFUNCTION()
	void OnRep_Value()
	{
		UE_LOG(LogTemp, Log, TEXT("OnRep_Value: %d"), Value);
	}

	UFUNCTION(Server, Reliable)
	void ServerRPC_Value(int32 _InValue);
};
ReplicatedSubObject.cpp
#include "./ReplicatedSubObject.h"
#include "Net/UnrealNetwork.h"

int32 UMySubObject::GetFunctionCallspace(UFunction* Function, FFrame* Stack)
{
	if( AActor* _Owner = GetTypedOuter<AActor>() ){
		return _Owner->GetFunctionCallspace(Function, Stack);
	}
	return(Super::GetFunctionCallspace(Function, Stack));
}

bool UMySubObject::CallRemoteFunction(UFunction* Function, void* Parms, FOutParmRec* OutParms, FFrame* Stack)
{
	AActor* _Owner = GetTypedOuter<AActor>();
	if (!_Owner) { return(false); }

	UNetDriver* _NetDriver = _Owner->GetNetDriver();
	if (!_NetDriver) { return(false); }

	_NetDriver->ProcessRemoteFunction(_Owner, Function, Parms, OutParms, Stack, this);
	return(true);
}

void UMySubObject::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, Value);
}

void UMySubObject::SetRelicated_Value(int32 _InValue)
{
	const AActor* _OwnerActor = GetTypedOuter<AActor>();

	if (_OwnerActor->HasAuthority()) {
		Value = _InValue;
		return;
	}

	check(_OwnerActor->GetLocalRole() == ROLE_AutonomousProxy);
	ServerRPC_Value(_InValue);
}

void UMySubObject::ServerRPC_Value_Implementation(int32 _InValue)
{
	SetRelicated_Value(_InValue);
}

実装側キャラクタークラスは以下のようになります。
レプリケートサブオブジェクトもAddReplicatedSubObject()だけではなくレプリケートする設定にしないとなりません。

AMyCharacter.h
UCLASS()
class SAMPLE00_API AMyCharacter: public ACharacter
{
	GENERATED_BODY()

    ..省略..
public:
	// ネットワークレプリケーションに使用されるプロパティを返す
	virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const override;

	UPROPERTY(Replicated)
	TObjectPtr<UMySubObject> SubObject;

};
AMyCharacter.cpp
AMyCharacter::AMyCharacter()
{
	// ReplicatedSubObjectを使う場合に必要な設定
	bReplicateUsingRegisteredSubObjectList = true;
}

void AMyCharacter::BeginPlay()
{

	// レプリケートサブオブジェクトを生成する
	if (HasAuthority()) {
		SubObject = NewObject<UMySubObject>(this);
		AddReplicatedSubObject(SubObject);
	}
}

void AMyCharacter::EndPlay()
{

	// レプリケートサブオブジェクトを始末
	if (HasAuthority()) {
		RemoveReplicatedSubObject(SubObject);
        SubObject = nullptr;
	}
}

void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(ThisClass, SubObject);
}

まとめ

UEのレプリケート処理の実装についてまとめました。Roleをかなり意識しないと意図した動作になりません。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?