Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

UnrealEngineer

[ UnrealEngine5 ] 액터 리플리케이션 빈도와 연관성(UnrealInsights) 본문

언리얼엔진5

[ UnrealEngine5 ] 액터 리플리케이션 빈도와 연관성(UnrealInsights)

UnrealEngineer 2024. 8. 25. 11:57

- 언리얼 인사이트

언리얼 프로그램의 다양한 퍼포먼스를 체크할 수 있는 프로파일링 도구

 

1. 언리얼엔진 설치된 폴더 확인

2. 인사이트 프로그램의 작업표시줄 고정

3. 언리얼 에디터 실행 파일의 환경변수 경로 설정

4. 언리얼 에디터를 구동하기 위한 배치파일 제작

 

1. 바로가기 만들기

 

2. 환경변수 추가하기

 

3. 배치파일 생성

 

4. 배치파일을 열고 언리얼인사이트를 실행시킨 후 Connect 시킨다.

 

-NetTrace=1 -Trace=Net을 추가해줘야 새로운창 2개가 생성된다. 하나는 서버 하나는 클라이언트로 사용한다.

 

5. 언리얼 인사이트 활용

서버탭에서 OutGoing으로 설정하면 서버에서 클라이언트로 리플리케이션되는 정보를 알 수 있다.

- 패킷 데이터

- 번치 정보 (채널 데이터) → 캐릭터 정보와 게임스테이트 정보를 클라이언트로 복제하고 있음을 확인 할 수 있다.

 

클라이언트에서도 InComing 정보를 통해 캐릭터 정보와 게임스테이트 정보가 복제되어 들어오는 것을 확인할 수 있다.

 


 

- 액터 리플리케이션의 빈도(Frequency)

 클라이언트와 서버가 통신하는 빈도 

 

NetUpdateFrequency : 리플리케이션 빈도의 최대치로 설정되어 있다.

< 1초에 몇번 리플리케이션을 시도할지 지정한 값 >

< 기본값은 100이고 서버는 1초에 100번 리플리케이션을 시도함 >

< 빈도는 최대치로 설정되어 있을뿐 이를 보장하지는 않음 >

< 서버의 Tick Rate에 따라 리플리케이션이 발생하지만 서버의 성능에 따라 달라짐 >

< 서버의 성능이 네트워 빈도보다 낮은 경우, 서버의 성능으로 복제됨 >

(ex. 100으로 설정되어 있더라도 서버가 1초에 50번 밖에 동작할 수 없으면 50번만 진행됨)

< 일반적으로 그래픽 기능이 없는 데디케이티드 서버가 좋은 성능을 발휘함>

 

 

 

- 실습

+ 규칙적으로 움직이는 액터의 네트웍 통신 데이터를 줄이는 방법

+ NetUpdateFrequency 속성 값을 1로 설정

+ 데이터 공백을 클라이언트에서 보간하여 부드러운 움직임으로 구현

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BCPortal.generated.h"

UCLASS()
class BIKINICITY_API ABCPortal : public AActor
{
	GENERATED_BODY()
	
public:	
	ABCPortal();

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<class UStaticMeshComponent> StaticMeshComponent;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	TObjectPtr<class UBoxComponent> BoxComponent;

	// 서버에서 복제될 프로퍼티
	UPROPERTY(ReplicatedUsing = OnRep_ServerRotationYaw)
	float ServerRotationYaw;

	UPROPERTY()
	float RotationRate = 30.0f;

	// 콜백함수 선언
	UFUNCTION()
	void OnRep_ServerRotationYaw();

	// 서버로부터 리플리케이션 보간
	// 서버로부터 패킷을 받은 이후 경과된 시간을 체크하는 변수
	float ClientTimeSinceUpdate = 0.0f;

	// 서버로부터 데이터를 받고 그다음 데이터를 받는데까지 걸린 시간을 기록하는 변수
	float ClientTimeBetweenLastUpdate = 0.0f;
	

protected:
	virtual void BeginPlay() override;

public:	
	virtual void Tick(float DeltaTime) override;

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


#include "BCPortal.h"

#include "BikiniCity/BikiniCity.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "Net/UnrealNetwork.h"
#include "BikiniCity/BikiniCity.h"

ABCPortal::ABCPortal()
{
	PrimaryActorTick.bCanEverTick = true;

	// 액터의 복제를 허용
	bReplicates = true;

	// 서버에서 클라이언트로 복제되는 횟수를 최소화하도록 설정(기본값 100)
	NetUpdateFrequency = 1;

	BoxComponent = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComponent"));
	SetRootComponent(BoxComponent);

	StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent"));
	StaticMeshComponent->SetupAttachment(RootComponent);
}

// 클라이언트에서만 실행되는 콜백함수
void ABCPortal::OnRep_ServerRotationYaw()
{
	BC_LOG(LogBCNetwork, Log, TEXT("Yaw : %f"), ServerRotationYaw);

	// 클라이언트인 경우
	FRotator NewRotator = RootComponent->GetComponentRotation();

	NewRotator.Yaw = ServerRotationYaw;

	RootComponent->SetWorldRotation(NewRotator);

	// 다음 데이터를 받는 시간을 경과된 시간으로 설정
	ClientTimeBetweenLastUpdate = ClientTimeSinceUpdate;

	// 서버로 부터 데이터를 받았기 때문에 0으로 초기화 한다 (콜백함수가 다시 호출되는 것이 데이터를 받는 것)
	ClientTimeSinceUpdate = 0.0f;
}

void ABCPortal::BeginPlay()
{
	Super::BeginPlay();

	/*if (HasAuthority())
	{
		FTimerHandle Handle;
		GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([&]
		{
			ServerRotationYaw += 10.0f;
		}
		), 1.0f, true, 0.0f);

	}*/
}

void ABCPortal::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// 서버에서 실행
	if(HasAuthority())
	{
		AddActorLocalRotation(FRotator(0.0f, RotationRate * DeltaTime, 0.0f));

		// 서버의 회전값을 저장해준다.
		ServerRotationYaw = RootComponent->GetComponentRotation().Yaw;
	}
	else // 클라이언트 회전값 보간 (NetUpdateFrequency = 1)
	{
		// 서버로 부터 패킷을 받은 이후에 시간이 누적이 된다.
		ClientTimeSinceUpdate += DeltaTime;

		// 값이 0에 가까워지면 보간의 의미가 없기 때문에 return한다.
		if (ClientTimeBetweenLastUpdate < KINDA_SMALL_NUMBER) 
		{
			return;
		}

		// 다음 네트워크 패킷때 수신되어야할 회전값 계산
		const float EstimateRotationYaw = ServerRotationYaw + RotationRate * ClientTimeBetweenLastUpdate;

		// 현재 보간할 Ratio값 계산
		const float LerpRatio = ClientTimeSinceUpdate / ClientTimeBetweenLastUpdate;

		FRotator ClientRotator = RootComponent->GetComponentRotation();
		const float ClientNewYaw = FMath::Lerp(ServerRotationYaw, EstimateRotationYaw, LerpRatio);
		ClientRotator.Yaw = ClientNewYaw;
		RootComponent->SetWorldRotation(ClientRotator);
	}
}

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

	DOREPLIFETIME(ABCPortal, ServerRotationYaw);
}

 


 

- 액터 리플리케이션 연관성 설정

서버의 관점에서 현재 액터가 클라이언트의 커넥션에 관련된 액터인지 확인하는 작업

< 레벨에 멀리 존재하는 액터는 대부분 보이지도 들리지도 않고 플레이에 영향을 끼치지도 않기 때문이다>

● 대형 레벨에 존재하는 모든 액터 정보를 클라이언트에게 보내는 것은 불필요함

● 클라이언트와 연관성이 있는 액터만 체계적으로 모아 통신 데이터를 최소화하는 방법

 

< 즉 관련없는 액터의 정보까지 모두 리플리케이션하는 불필요한 작업을 최소화하기 위함 >

< 영향을 끼치는 ‘액터 세트’를 확보하는 것이 연관성의 주요 개념 >

 

- 액터 속성에 따른 연관성 판정을 위한 속성

AlwaysRelevant : 항상 커넥션에 대해 연관성을 가짐 (GameState, PlayerState)

NetUseOwnerRelevancy : 자신의 연관성은 오너의 연관성으로 판정함

OnlyRelevantToOwner : 오너에 대해서만 연관성을 가짐 (카메라 및 캐릭터와 연관이 없으면 복제X)

Net Cull Distance : 뷰어와의 거리에 따라 연관성 여부를 결정함

(기본값이 크게 설정되어 있음 150m)

 

※ 언리얼엔진은 편의성을 위해 IsRelevantFor()라는 가상함수를 제공한다.

 

// 연관성 설정
NetCullDistanceSquared = 100000.0f;

 

bool ABCPortal::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const
{
bool NetRelevantResult = Super::IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation);

// 결과가 false인 경우 로그 출력
if (!NetRelevantResult)
{
BC_LOG(LogBCNetwork, Log, TEXT("NotRelevant : [%s] %s"), *RealViewer->GetName(), *SrcLocation.ToCompactString());
}
else
{
BC_LOG(LogBCNetwork, Log, TEXT("OnRelevant : [%s] %s"), *RealViewer->GetName(), *SrcLocation.ToCompactString());
}

return NetRelevantResult;
}

 

NetCullDistanceSquared을 넘어서서까지 멀리가면 연관성이 없어진다.