언리얼 프로그램의 다양한 퍼포먼스를 체크할 수 있는 강력한 프로파일링 도구
프로그램 프로파일링 뿐만 아니라 네트워크 상태도 확인할 수 있음 (Network Insights)
- 언리얼 인사이트 실행 파일 위치
C:\Program Files\Epic Games\UE_5.5\Engine\Binaries\Win64
UnrealInsights.exe 파일 존재환경 변수의 Path에 해당 경로를 추가합니다.
- 배치파일 제작
bat 확장자로 파일을 만든 후, 아래와 같이 입력하여 실행
UnrealEditor.exe %cd%\ArenaBattle.uproject -NetTrace=1 -Trace=Net
- NetTrace, Trace
Networking Insights를 실행하기 위한 옵션언리얼 에디터 우측 하단에서 사진과 같이 언리얼 인사이트를 실행하여 에디터와 연결합니다.
클라이언트와 서버간에 진행되는 통신 빈도
NetUpdateFrequency: 리플리케이션 빈도의 최대치 설정
1초당 몇 번 리플리케이션을 시도할지 지정한 값
기본 값은 100, 이론적으로 서버는 1/100초 간격으로 리플리케이션을 시도함
네트워크 빈도는 최대치일 뿐 이를 보장하진 않음
서버의 Tick Rate에 따라 리플리케이션이 발생하지만, 서버의 성능에 따라 달라짐
서버의 성능이 네트워크 빈도보다 낮은 경우, 서버의 성능으로 복제됨
일반적으로 그래픽 기능이 없는 데디케이티드 서버가 더 좋은 성능을 발휘함
규칙적으로 움직이는 액터의 네트워크 통신 데이터를 줄이는 예제
NetUpdateFrequency 속성 값을 1로 설정
데이터 공백을 클라이언트에서 부드러운 움직임으로 보완하기
이전 복제된 데이터에 기반해 현재 틱에서의 회전 값을 예측
클라이언트에서 예측된 값을 보간해 회전

참고로 빌드 구성을 DebugGame Editor로 하고 빌드하면 uproject 파일로 프로젝트를 실행할 땐 반영되지 않습니다.
Development Editor로 설정되어 있는지 확인하고 빌드합니다!
유의미한 업데이트가 없으면 빈도를 줄여서 부하를 줄이는 기법
MinNetUpdateFrequency: 리플리케이션 빈도의 최소치 설정을 사용함
기본 값은 2
최소 값과 최대 값 사이에서 현재 액터에 맞는 최적의 전송 타이밍을 설정함
[SystemSettings]
net.UseAdaptiveNetUpdateFrequency=1
서버의 관점에서 현재 액터가 클라이언트의 커넥션에 관련된 액터인지 확인하는 작업
대형 레벨에 존재하는 모든 액터 정보를 클라이언트에게 보내는 것은 불필요함
클라이언트와 연관있는 액터만 체계적으로 모아 통신 데이터를 최소화하는 방법

연관성 판별을 위한 특별한 액터의 정의
오너(Owner)의 정의
액터를 소유하는 액터, 최상단의 소유 액터를 의미

서버에서는 틱마다 모든 커넥션과 액터에 대해 연관성을 점검함
클라이언트의 뷰어와 관련있고, 뷰어와의 일정 거리 내에 있는 액터를 파악
해당 액터 묶음의 정보를 클라이언트에게 전송

// 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)
{
// 이미 뷰타겟이나 뷰어의 IsOwnedBy() 검사를 실패한 경우인데 옵션이 켜져있어 연관성 없음
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;
}
// 거리에 따른 연관성 옵션 확인(디폴트론 켜져있음)
// SrcLocation은 플레이어 컨트롤러인 뷰어의 위치
return !GetDefault<AGameNetworkManager>()->bUseDistanceBasedRelevancy ||
IsWithinNetRelevancyDistance(SrcLocation);
}
bool AActor::IsWithinNetRelevancyDistance(const FVector& SrcLocation) const
{
return FVector::DistSquared(SrcLocation, GetActorLocation()) < GetNetCullDistanceSquared();
}
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);
}
bool APlayerController::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
// 클라이언트에 하나 밖에 없기 때문에 대부분 연관성을 통과함
return ( this==RealViewer );
}
https://github.com/dnjfs/ArenaBattle_Network/commit/6cb37c39d6cbcbb811674e8bcdf3187f1ecbf414
NetCullDistanceSquared
거리에 따른 연관성 여부를 위한 거리 값을 제곱한 값
IsNetRelevantFor()
해당 함수를 오버라이드하여 커스텀하게 연관성 검사 방식 변경 가능