언리얼 인사이트
- 언리얼 프로그램의 다양한 퍼포먼스를 체크할 수 있는 강력한 프로파일링 도구
- 언리얼 엔진에 포함되어 있음
- 프로그램 프로파일링 뿐만 아니라 네트웍 상태도 확인할 수 있다.
액터 리플리케이션의 빈도
주요 액터에 설정된 빈도 값
네트웍 데이터 줄이기
- 규칙적으로 움직이는 액터의 네트웍 통신 데이터를 줄이는 예제
- NetUpdateFrequency 속성 값을 1로 설정
- 데이터 공백을 클라이언트에서 부드러운 움직임으로 보완하기
이전 복제된 데이터에 기반해 현재 틱에서의 회전 값을 예측
클라이언트에서 예측된 값을 보간해 회전
ABFountain 의 소스 코드
// Called every frame
void AABFountain::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (HasAuthority())
{
AddActorLocalRotation(FRotator(0.0f, RotationRate * DeltaTime, 0.0f));
ServerRotationYaw = RootComponent->GetComponentRotation().Yaw;
}
else
{
ClientTimeSinceUpdate += DeltaTime;
if (ClientTimeBetweenLastUpdate < KINDA_SMALL_NUMBER)
{
return;
}
const float EstimateRotationYaw = ServerRotationYaw + RotationRate * ClientTimeBetweenLastUpdate;
const float LerpRatio = ClientTimeSinceUpdate / ClientTimeBetweenLastUpdate;
FRotator ClientRotator = RootComponent->GetComponentRotation();
const float ClientNewYaw = FMath::Lerp(ServerRotationYaw, EstimateRotationYaw, LerpRatio);
ClientRotator.Yaw = ClientNewYaw;
RootComponent->SetWorldRotation(ClientRotator);
}
}
float RotationRate = 30.0f;
float ClientTimeSinceUpdate = 0.0f;
float ClientTimeBetweenLastUpdate = 0.0f;
선언부에 위와 같이 변수를 선언해준 뒤
NetUpdateFrequency = 1.0f;
으로 생성자에서 설정한다.
void AABFountain::OnRep_ServerRotationYaw()
{
AB_LOG(LogABNetwork, Log, TEXT("Yaw : %f"), ServerRotationYaw);
FRotator NewRotator = RootComponent->GetComponentRotation();
NewRotator.Yaw = ServerRotationYaw;
RootComponent->SetWorldRotation(NewRotator);
ClientTimeBetweenLastUpdate = ClientTimeSinceUpdate;
ClientTimeSinceUpdate = 0.0f;
}
레플리케이션 함수에서
ClientTimeBetweenLastUpdate 에 레플리케이션이 불리기 전까지의 시간을 저장해주고 다시 0으로 초기화 해준다.
적응형 네트워크 업데이트
연관성(Relevancy) 이란?
- 서버의 관점에서 현재 액터가 클라이어트의 커넥션에 관련된 액터인지 확인하는 작업
- 대형 레벨에 존재하는 모든 액터 정보를 클라이언트에게 보내는 것은 불필요함.
- 클라이언트와 연관있는 액터만 체계저긍로 모아 통신 데이터를 최소화하는 방법
++)
인기 게임 배틀그라운드 를 생각하면 편하다. 반경 1km 이내의 적만 보이고 그 이상으로는 렌더링 되지 않는다.
연관성에 관련된 다양한 속성
연관성의 점검
액터 속성에 따른 연관성 판정을 위한 속성
- AlwaysRelevant : 항상 커넥션에 대해 연관성을 가짐
- NetUseOwnerRelevacy : 자신의 연광성은 오너의 연관성으로 판정함.
- OnlyRelevantToOwner : 오너에 대해서만 연관성을 가짐
- Net Cull Distance : 뷰어와의 거리에 따라 연관성 여부를 결정함 (계산을 적게 하기 위해 제곱 사용)
Relevant 코드 살펴보기
Actor
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; // 보이지 않는데 루트컴포넌트가 없거나 충돌이 활성화 되어 있지 않다면 테스트 필요 X
}
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);
// 거리에 따른 연관성 검사 옵션을 활성화 할지 옵션을 통해 확인
//
}
Pawn
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);
}
PlayerController
bool APlayerController::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
return ( this==RealViewer ); // 클라이언트에 하나만 존재하므로 연관성 통과
}
'언리얼엔진5 > [Part3] 이득우의 언리얼 프로그래밍' 카테고리의 다른 글
[이득우의 언리얼 프로그래밍 Part3 필기] 8. RPC 기초 (0) | 2024.05.07 |
---|---|
[이득우의 언리얼 프로그래밍 Part3 필기] 7. 액터 리플리케이션 로우레벨 플로우 (0) | 2024.05.06 |
[이득우의 언리얼 프로그래밍 Part3 필기] 5. 액터 리플리케이션 기초 (0) | 2024.04.29 |
[이득우의 언리얼 프로그래밍 Part3 필기] 4. 액터의 역할과 커넥션 핸드셰이킹 (0) | 2024.04.27 |
[이득우의 언리얼 프로그래밍 Part3 필기] 3. 커넥션과 오너십 (0) | 2024.04.25 |