Sparta Unreal 부트캠프 72일차

정찬호·2026년 3월 16일

Unreal Engine 멀티플레이어 게임 개발 수강 중

언리얼 네트워크에서 사용되는 데이터 단위

넷커넥션(NetConnection)

  • 모든 멀티플레이 관련 데이터들이 드나드는 “통로”.
  • 서버는 접속한 클라이언트의 갯수만큼 넷커넥션을 가집니다.
  • 클라이언트는 서버와의 통신을 위한 단 하나의 넷커넥션만 가집니다.

채널(Channel)

  • 넷커넥션 개체의 Channels라는 속성이 있습니다.
    이를통해 넷커넥션 개체가 채널들을 관리함을 알 수 있습니다.
  • ActorChannel, ControlChannel, VoiceChannel들을 관리 중입니다. 특히 이 중에서 ActorChannel이 Actor Replication과 관련된 채널입니다.

패킷(Packet)

  • 네트워크에서 사용되는 통상적인 데이터 단위입니다.

번치(Bunch)

  • 언리얼에서 사용하는 특별한 패킷 단위입니다.

GameMode의 StartPlay가 실행되지 않으면 다른 모든 액터의 BeginPlay가 실행되지 않습니다.
GameMode는 서버에만 존재한는데 멀티 환경에서 다른 Clinet들의 BeginPlaye는 어떻게 실행되는 걸까요?
GameMode는 본인이 직접 게임의 시작을 하는 것이 아니라 GameState에 명령을 내립니다.
그리고 GameState는 모든 Client에 복사됩니다. 때문에 게임의 시작을 알릴 수 있습니다.

GameState BeginPlay 함수 호출 살펴보기

출력 로그 - server

LogDXNet: [Server] ADXGameModeBase::ADXGameModeBase 
LogDXNet: [Server] ADXGameStateBase::ADXGameStateBase 
LogDXNet: [Server] ADXGameModeBase::StartPlay Begin
LogDXNet: [Server] ADXGameStateBase::HandleBeginPlay Begin
LogDXNet: [Server] ADXGameStateBase::HandleBeginPlay End
LogDXNet: [Server] ADXGameModeBase::StartPlay End
LogDXNet: [Server] ADXGameModeBase::PreLogin Begin
LogDXNet: [Server] ADXGameModeBase::PreLogin End
LogDXNet: [Server] ADXGameModeBase::PreLogin End
LogDXNet: [Server] ADXGameModeBase::Login Begin
LogDXNet: [Server] ADXPlayerController::ADXPlayerController 
LogDXNet: [Server] ADXPlayerController::PostInitializeComponents Begin
LogDXNet: [Server] ADXPlayerController::PostInitializeComponents End
LogDXNet: [Server] ADXPlayerController::BeginPlay Begin
LogDXNet: [Server] ADXPlayerController::BeginPlay End
LogDXNet: [Server] ADXGameModeBase::Login End
LogDXNet: [Server] ADXGameModeBase::PostLogin Begin
LogDXNet: [Server] ADXPlayerCharacter::ADXPlayerCharacter 
LogDXNet: [Server] ADXGameModeBase::PostLogin Client Connection: IpConnection_2
LogDXNet: [Server] ADXGameModeBase::PostLogin End
LogDXNet: [Server] ADXGameModeBase::PreLogin Begin
LogDXNet: [Server] ADXGameModeBase::PreLogin End
LogDXNet: [Server] ADXGameModeBase::PreLogin End
LogDXNet: [Server] ADXGameModeBase::Login Begin
LogDXNet: [Server] ADXPlayerController::ADXPlayerController 
LogDXNet: [Server] ADXPlayerController::PostInitializeComponents Begin
LogDXNet: [Server] ADXPlayerController::PostInitializeComponents End
LogDXNet: [Server] ADXPlayerController::BeginPlay Begin
LogDXNet: [Server] ADXPlayerController::BeginPlay End
LogDXNet: [Server] ADXGameModeBase::Login End
LogDXNet: [Server] ADXGameModeBase::PostLogin Begin
LogDXNet: [Server] ADXPlayerCharacter::ADXPlayerCharacter 
LogDXNet: [Server] ADXGameModeBase::PostLogin Client Connection: IpConnection_2
LogDXNet: [Server] ADXGameModeBase::PostLogin Client Connection: IpConnection_3
LogDXNet: [Server] ADXGameModeBase::PostLogin End

출력 로그 - client

LogDXNet: [Client01] ADXPlayerController::ADXPlayerController 
LogDXNet: [Client01] ADXPlayerController::PostInitializeComponents Begin
LogDXNet: [Client01] ADXPlayerController::PostInitializeComponents End
LogDXNet: [Client01] ADXPlayerController::OnActorChannelOpen Begin
LogDXNet: [Client01] ADXPlayerController::OnActorChannelOpen End
LogDXNet: [Client01] ADXPlayerController::PostNetInit Begin
LogDXNet: [Client01] ADXPlayerController::PostNetInit Server Connection: IpConnection_0
LogDXNet: [Client01] ADXPlayerController::PostNetInit End
LogDXNet: [Client01] ADXPlayerCharacter::ADXPlayerCharacter 
LogDXNet: [Client01] ADXGameStateBase::ADXGameStateBase 
LogDXNet: [Client01] ADXGameStateBase::OnRep_ReplicatedHasBegunPlay Begin
LogDXNet: [Client01] ADXPlayerController::BeginPlay Begin
LogDXNet: [Client01] ADXPlayerController::BeginPlay End
LogDXNet: [Client01] ADXGameStateBase::OnRep_ReplicatedHasBegunPlay End
LogDXNet: [Client01] ADXPlayerCharacter::ADXPlayerCharacter 
LogDXNet: [Client02] ADXPlayerController::ADXPlayerController 
LogDXNet: [Client02] ADXPlayerController::PostInitializeComponents Begin
LogDXNet: [Client02] ADXPlayerController::PostInitializeComponents End
LogDXNet: [Client02] ADXPlayerController::OnActorChannelOpen Begin
LogDXNet: [Client02] ADXPlayerController::OnActorChannelOpen End
LogDXNet: [Client02] ADXPlayerController::PostNetInit Begin
LogDXNet: [Client02] ADXPlayerController::PostNetInit Server Connection: IpConnection_1
LogDXNet: [Client02] ADXPlayerController::PostNetInit End
LogDXNet: [Client02] ADXPlayerCharacter::ADXPlayerCharacter 
LogDXNet: [Client02] ADXGameStateBase::ADXGameStateBase 
LogDXNet: [Client02] ADXGameStateBase::OnRep_ReplicatedHasBegunPlay Begin
LogDXNet: [Client02] ADXPlayerController::BeginPlay Begin
LogDXNet: [Client02] ADXPlayerController::BeginPlay End
LogDXNet: [Client02] ADXGameStateBase::OnRep_ReplicatedHasBegunPlay End
LogDXNet: [Client02] ADXPlayerCharacter::ADXPlayerCharacter 

GameState 클래스와 BeginPlay()함수 호출 살펴보기

  1. StartPlay에서 HandleBeginPlay를 호출
  2. HandleBeginPlay에서 bReplicatedHasBegunPlay 속성을 수정해서 클라이언트로의 프로퍼티 리플리케이션을 유도
  3. 동시에 서버에서도 WorldSettings::NotifyBeginPlay() 함수 호출
  4. 클라이언트에서 GameState::OnRep_ReplicatedHasBegunPlay() 함수 호출, 이 함수에서 WorldSettings::NotifyBeginPlay()이 호출됩니다.

AGameStateBase 클래스에 bReplicatedHasBegunPlay 속성이 존재합니다.

// GameStateBase.h

class AGameStateBase : public AInfo
{
	...
	
	/** Replicated when GameModeBase->StartPlay has been called so the client will also start play */
	UPROPERTY(Transient, ReplicatedUsing = OnRep_ReplicatedHasBegunPlay)
	bool bReplicatedHasBegunPlay;
	
	...
	
}

ReplicatedUsing

  • Replicated와 유사하지만 Replicate 시 지정된 함수를 호출하는 것


HandleBeginPlay()에서 bReplicatedHasBegunPlay를 수정해서 OnRep_ReplicatedHasBegunPlay 호출 유도
동시에 서버에서 NotifyBeginPlay() 호출
...
// GameStateBase.cpp
...
void AGameStateBase::HandleBeginPlay()
{
	bReplicatedHasBegunPlay = true;        // 클라이언트 모든 액터의 BeginPlay() 함수가 호출되게끔 함

	GetWorldSettings()->NotifyBeginPlay(); // 서버 모든 액터의 BeginPlay() 함수가 호출되게끔 함
	...
}
// WorldSettings.cpp

void AWorldSettings::NotifyBeginPlay()
{
	...
	if (!World->bBegunPlay)
	{
		for (FActorIterator It(World); It; ++It)
		{
			...
			It->DispatchBeginPlay(bFromLevelLoad);
		}

		...
	}
}

시작 관련 주요 이벤트 함수

PostNetInit()

  • 서번에서 해당 액터의 속성 등 변경된 내용 세팅이 모두 완료된 뒤 클라이언트에 복제되었을 대 호출되는 함수입니다.
  • 멀티플레이에서는 BeginPlay() 함수가 호출되려면 해당 액터에 대한 서버에서의 처리가 모두 완료되어야 합니다.
  • 때문에 PostNetIint() 함수가 항상 먼저 실행된 후 StartPlay() 함수에 의해 BeginPlay() 함수가 호출됩니다.

PostInitializeComponents()

  • 해당 액터에 부착된 컴포넌트가 모두 준비된 상태에 호출됩니다.

StartPlay()

  • 게임의 시작을 지시하는 함수입니다.

BeginPlay()

  • 게임 모드의 StartPlay() 함수를 통해 게임이 시작될 때 모든 액터에서 호출되는 함수입니다.

APawn::PossessedBy() 함수와 APawn::Unpossessed() 함수 살펴보기

Owner 설정 로직을 담당하는 부분입니다.
Owner 설정 로직은 액터가 어떤 클라이언트이 넷커넥션에 속하게 되는 지를 결정하는 중요한 로직입니다.

void APawn::PossessedBy(AController* NewController)
{
	SetOwner(NewController);
	
	...
}

void APawn::UnPossessed()
{
	AController* const OldController = Controller;

	ForceNetUpdate();

	SetPlayerState(nullptr);
	SetOwner(nullptr);
	Controller = nullptr;

	...
}

클라이언트에서는 OnPossess()가 호출되지 않지만 Actor::Owner는 ReplicatedUsing 키워드가 달린 속성이기에 Owner가 초기화 되면 OnRep_Owner() 함수가 클라이언트에서 호출된다고 합니다.

  1. PossessedBy() 함수는 서버에서만 호출되며, Owner 속성이 수정됨
  2. 서버에서 Owner 속성이 수정되어 OnRep_Owner() 함수가 호출됨
  3. Owner 속성이 Replicated 되고 난 뒤에 PostNetInit() 함수가 호출된다는 점. 즉, 모든 네트워크 관련 속성들이 초기화 된 뒤에 호출됨.

매크로를 통한 로컬 룰과 리모트 룰 출력

// DedicatedX.h

...

#pragma region NetLogging

...

#define LOCAL_ROLE_TCHAR *(UEnum::GetValueAsString(TEXT("Engine.ENetRole"), GetLocalRole()))
#define REMOTE_ROLE_TCHAR *(UEnum::GetValueAsString(TEXT("Engine.ENetRole"), GetRemoteRole()))
#define DX_LOG_ROLE(LogCat, Verbosity, Format, ...) UE_LOG(LogCat, Verbosity, TEXT("[%s][%s/%s] %s %s"), NETMODE_TCHAR, LOCAL_ROLE_TCHAR, REMOTE_ROLE_TCHAR, FUNCTION_TCHAR, *FString::Printf(Format, ##__VA_ARGS__))

#pragma endregion

빙의 전후 캐릭터의 리모트 룰 비교

LogDXNet: [Server] ADXPlayerCharacter::ADXPlayerCharacter 
LogDXNet: [Server] ADXPlayerController::OnPossess Begin
LogDXNet: [Server][ROLE_Authority/ROLE_SimulatedProxy] <- 여기 ADXPlayerCharacter::PossessedBy Begin
LogDXNet: [Server] ADXPlayerCharacter::PossessedBy There is no OwnerActor.
LogDXNet: [Server] ADXPlayerCharacter::PossessedBy OwnerActor Name: BP_PlayerController_C_1
LogDXNet: [Server][ROLE_Authority/ROLE_AutonomousProxy] <- 여기 ADXPlayerCharacter::PossessedBy End
LogDXNet: [Server] ADXPlayerController::OnPossess End
LogDXNet: [Server] ADXGameModeBase::PostLogin Client Connection: IpConnection_2
LogDXNet: [Server] ADXGameModeBase::PostLogin Client Connection: IpConnection_3

Property Replication

NetLoadOnclient 속성

레벨에 존재하는 모든 액터의 정보를 서버에서 클라이언트로 복제하는 것은 매우 비효율적입니다.
레벨 디자인을 통해 레벨에 고정적으로 배치되는 액터는 NetLoadOnClient 속성을 true로 지정해서 모든 클라이언트가 스스로 스폰하도록 해야합니다.
각각 알아서 생성하게 둔다.
플레이어의 로그인에 따라 스폰되는 액터(PlayerController, Pawn 등)와 혼동하면 안 됩니다.
지형 같은 것들이 이에 해당할 것 같습니다.

Replication Notify의 특징

  • 서버에서는 실행되지 않고, 클라이언트에서만 실행됩니다.
  • 서버에서도 해당 로직이 실행되어야 한다면, 명시적으로 호출해 줘야 합니다.

C++ OnRep_ 함수 VS Blueprint RepNotify 함수

  • 속성 값이 변경되어서 변경된 값이 클라이언트에 복제되는 시점에 콜백함수를 바인드할 수 있습니다.
  • 이것을 Replication Notify 함수라고 합니다. C++에서는 OnRep_, 블루프린트에선 RepNotify
C++ OnRep_ 함수Blueprint RepNotify 함수
클라이언트에서만 호출서버와 클라이언트 모두에서 호출
명시적 호출 가능명시적 호출 불가능
속성 값이 변경될 때만 호출서버는 항시 호출되고,클라이언트는 속성 값 변경될 때만 호출

Development Editor에 따른 최적화 막기

UE_DISABLE_OPTIMIZATION
~
UE_ENABLE_OPTIMIZATION

위 매크로 사이의 함수는 Development Editor 빌드에 따른 최적화를 막아 Debug Game Editor를 돌리는 것처럼 돌아갑니다.

NetUpdateFrequency

NetUpdateFrequency

  • 액터 레플리케이션 빈도의 최대치로 1초당 몇 번 레플리케이션을 시도할지 지정한 값입니다..
  • 기본값은 100으로 되어있습니다.
  • 즉 이론적으로 서버는 1/100초 간격으로 레플리케이션을 시도합니다.
  • GetNetUpdateFrequency()로 가져올 수 있습니다.

주의 보장되지 않습니다.
NetUpdateFrequency는 최대치일 뿐 이를 보장하진 않습니다.
서버의 Tick Rate에 따라 레플리케이션이 발생하지만, 서버의 성능에 따라 달라집니다.
서버의 성능이 NetUpdateFrequency보다 낮은 경우, 서버의 성능에 맞춰서 복제됩니다.
그래서 그래픽 기능이 없는 데이케이티드 서버가 더 좋은 성능을 발휘 가능하다고 합니다.

주요 액터의 NetUpdateFrequency 속성 기본값

TypeFrequency
Actor100
Pawn100
PlayerController100
GameState10
PlayerState1

네트워크 부하 줄이는 방법

어떤 액터의 서버의 위치와 클라이언트의 위치가 서로 동기화되어야 한다고 가정해 봅니다.
동기화하는 방법은 두 가지가 있습니다.
1. 매 틱마다 액터의 서버 위치를 클라이언트 측으로 레플리케이션해서 동기화
2. 액터의 처음 서버 위치와 속도 값을 안다면, 이를 이용해서 예측을 통해 동기화

Relevancy

레벨에 있는 모든 액터의 정보를 모든 클라이언에게 실시간으로 전송하기엔 꽤 큰 부하가 걸립니다.(N*N)
연관성을 판단하는 여러 기준으로 토대로 서버 측에서 해당 클라이언트 커넥션과 연관성이 있는 액터만 레플리케이션하는 최적화가 필요합니다.

액터 클래스의 연관성을 판단하는 기준

Owner

  • 해당 액터를 소유하고 있는 액터. 해당 액터와 Owner는 연관성이 있다고 판단.
  • ex) 무기 액터를 소유하고 있는 캐릭터

Instigator

  • 해당 액터에게 영향을 끼친 폰. 해당 액터와 Instigator는 연관성이 있다고 판단.
  • ex) 데미지를 가한 폰

AlwaysRelevant

  • 해당 액터가 모든 클라이언트에게 항상 연관성이 있게끔 설정할 때 사용합니다.
  • ex) 엄청나게 큰 보스 몬스터가 있고, 레벨 내의 모든 플레이어에게 레플리케이션 할 때.

NetUseOwnerRelevancy

  • 오너 액터의 연관성으로 해당 액터의 연관성을 대신할 때 사용합니다.
  • ex) 캐릭터가 소유하고 있는 무기 액터. 캐릭터가 안 보이는데 무기만 보일 리 없기 때문.

OnlyRelevantToOwner

  • 오너 액터에게만 연관성을 가집니다. 즉, 다른 액터와는 연관성 없습니다.
  • ex) 길라잡이 액터. 굳이 다른 플레이어가 내 길라잡이 액터들을 볼 필요는 없습니다.

NetCullDistance

  • 뷰어와의 거리에 따라 연관성 여부를 결정합니다.
  • ex) 아주 먼 곳에 위치한 나무의 나뭇잎 액터. 가까우면 레플리케이션하고, 멀면 안 하게끔 합니다.

폰 클래스의 연관성을 판단하는 기준
플레이어 컨트롤러에 빙의된 폰이 움직이면서 어떤 액터가 레플리케이션될지 결정됩니다.

Viewer

  • 클라이언트 커넥션이 소유하고 있는 플레이어 컨트롤러를 가리킵니다.

ViewTarget

  • 플레이어 컨트롤러가 빙의한 폰.
  • Viewer에 의해 빙의된 ViewTarget이 레벨을 돌아다니는 것입니다.
    즉, 어떤 액터가 Viewer & ViewTarget 액터와 연관성이 있느냐 없느냐가 이번 단원 내용입니다.

IsNetRelevantFor() 함수

bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const;

Relevant 관계를 확인하는 함수입니다.
서버에서만 호출됩니다.

RealViewer: 데이터를 받을 대상, 관찰자 본인. 일반적으로는 클라이언트의 PlayerController
ViewTarget: 관찰자가 현재 보고 있는 대상. PlayerController의 경우 빙의 중인 Pawn인 경우가 많습니다.
SrcLocation: 판단의 기준이 되는 위치입니다.

APlayerController 클래스의 IsNetRelevantFor() 함수

// APlayerController.cpp

bool APlayerController::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
	return ( this==RealViewer );
}
  • 클라이언트 커넥션은 본인이 소유하고 있는 플레이어 컨트롤러(RealViewer)를 알고 있습니다.
  • 플레이어 컨트롤러(RealViewer)와 this가 같다면 연관성이 있는 것으로 판단합니다.

APawn 클래스의 IsNetRelevantFor() 함수

// APawn.cpp

bool APawn::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
	CA_SUPPRESS(6011);
	if (bAlwaysRelevant || RealViewer == Controller || IsOwnedBy(ViewTarget) || IsOwnedBy(RealViewer) || this == ViewTarget || ViewTarget == GetInstigator()
		|| IsBasedOnActor(ViewTarget) || (ViewTarget && ViewTarget->IsBasedOnActor(this)))
	{
		return true;
	}
	else if ((IsHidden() || bOnlyRelevantToOwner) && (!GetRootComponent() || !GetRootComponent()->IsCollisionEnabled())) 
	{
		return false;
	}
	else
	{
		UPrimitiveComponent* MovementBase = GetMovementBase();
		AActor* BaseActor = MovementBase ? MovementBase->GetOwner() : nullptr;
		if ( MovementBase && BaseActor && GetMovementComponent() && ((Cast<const USkeletalMeshComponent>(MovementBase)) || (BaseActor == GetOwner())) )
		{
			return BaseActor->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
		}
	}

	return !GetDefault<AGameNetworkManager>()->bUseDistanceBasedRelevancy ||
			IsWithinNetRelevancyDistance(SrcLocation);
}
  • RealViewer(ClientConnection이 소유하고 있는 PlayerController) == Controller
  • bAlwaysRelevant == true
  • ViewTarget이나 REalViewer에 속해져 있음. IsOwnedBy
  • ViewTarget == this 혹은 ViewTarget == Instigator
  • IsBasedOnActor(ViewTarget) == true
  • (ViewTarget && ViewTarget->IsBasedOnActor(this) == true

AActor 클래스의 IsNetRelevantFor() 함수

// ActorReplication.cpp

bool AActor::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
	if (bAlwaysRelevant || IsOwnedBy(ViewTarget) || IsOwnedBy(RealViewer) || this == ViewTarget || ViewTarget == GetInstigator())
	{
		return true;
	}
	else if (bNetUseOwnerRelevancy && Owner)
	{
		return Owner->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
	}
	else if (bOnlyRelevantToOwner)
	{
		return false;
	}
	else if (RootComponent && RootComponent->GetAttachParent() && RootComponent->GetAttachParent()->GetOwner() && (Cast<USkeletalMeshComponent>(RootComponent->GetAttachParent()) || (RootComponent->GetAttachParent()->GetOwner() == Owner)))
	{
		return RootComponent->GetAttachParent()->GetOwner()->IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);
	}
	else if(IsHidden() && (!RootComponent || !RootComponent->IsCollisionEnabled()))
	{
		return false;
	}

	if (!RootComponent)
	{
		UE_LOG(LogNet, Warning, TEXT("Actor %s / %s has no root component in AActor::IsNetRelevantFor. (Make bAlwaysRelevant=true?)"), *GetClass()->GetName(), *GetName() );
		return false;
	}

	return !GetDefault<AGameNetworkManager>()->bUseDistanceBasedRelevancy ||
			IsWithinNetRelevancyDistance(SrcLocation);
}
  • APawn과 비슷합니다.
  • IsBasedOnActor 관련 체크가 없습니다.
  • bNetUseOwnerRelevancy == true, Owner이 존재할 때 Owner의 IsNetRelevantFor를 호출합니다.

NetPriority

클라이언트에 보낼 수 있는 대역폭(NetBandwidth)은 한정되어 있습니다.
클라이언트에 보낼 액터 중, NetPriority 값이 높은 액터의 데이터를 우선 전달하도록 설계되어 있습니다.
액터에 설정된 NetPriority 값을 활용해 전송 순서를 결정합니다.

NetPriority
PlayerController3
Pawn2
Actor1
  • NetPriority 속성 값은 순서를 정하는 데만 사용됩니다.
  • 액터의 현재 NetPriority 속성 값은 AActor::GetNetPriority() 함수에서 계산됩니다.
    • NetCullDistance 같은 연관성 관련 속성들이 반영됩니다.
    • 직전 패킷을 보낸 후 경과 시간과 가중치, 이전 NetPriority 값 등을 곱해서 계산합니다.
    • 포화 상태가 아니라면 계산된 NetPriority 값을 기준으로 정렬된 순서대로 레플리케이션합니다.

가중치
여러 가지 기준에 따라 가중치를 결정합니다.
1. 해당 액터가 ViewTarget인 경우 -> 4.f
2. 해당 액터가 ViewTarget 액터에게 영향을 받은 경우 -> 4.f
3. ViewTarget이 바라보는 방향에 해당 액터가 있고 가까운 경우 → 2.f
4. ViewTarget이 바라보는 방향에 해당 액터가 있지만 적당히 먼 경우 → 0.4f
5. ViewTarget이 바라보는 방향에 해당 액터가 없고 적당히 먼 경우 → 0.2f

포화 상태

  • 액터 관련 데이터가 너무 커서 대역폭을 넘어선 경우를 포화 상태라고 합니다.
    현재 NetPriority 값이 가장 낮은 액터는 다음 서버 틱에서 리플리케이션 됩니다.

NetDormancy(휴면)

휴면 상태인 액터는 더 이상 프로퍼티 레플리케이션이나 RPC가 동작하지 않습니다.

액터가 레플리케이트 되었지만, 자주 수정되진 않을 때 활용합니다.
플레이어 캐릭터처럼 자주 수정되는 액터를 휴면 상태로 두면 오히려 오버헤드가 발생합니다.

네트워크 휴면 상태들

Conditional Property Replication

프로퍼티는 한 번 레플리케이션에 등록되면 다시 해제시킬 수가 없습니다.
그래서 DOREPLIFETIME라고 합니다.

Conditional Property Replication은 레플리케이션에 등록된 프로퍼티를
조건식을 통해 좀 더 세밀하게 조정할 수 있게끔 해줍니다.
단점은 조건식 값이 너무 자주 바뀐다면 오버헤드 발생할 수 있습니다.

FlushNetDormancy()

Dormancy를 해제? 초기화시켜주는 함수
Server에서 호출

profile
게임 개발 지망생입니다.

0개의 댓글