728x90
반응형
- 정리
- 액터 리플리케이션 빈도와 연관성
- 언리얼 인사이트 도구의 사용 방법의 학습
- 액터 리플리케이션 빈도 속성 조절에 따른 변화 확인
- 액터 리플리케이션 연관성 설정에 따른 변화 확인
- 액터 리플리케이션 빈도와 연관성
- 언리얼 인사이트
- 언리얼 프로그램의 다양한 퍼포먼스를 체크할 수 있는 강력한 프로파일링 도구
- 언리얼 엔진에 포함되어 있음
- 프로그램 프로파일링 뿐만 아니라 네트웍 상태도 확인할 수 있음 (Network Insights)
- 액터 리플리케이션의 빈도(Frequency)
- 클라이언트와 서버간에 진행되는 통신 빈도
- NetUpdateFrequency : 리플리케이션 빈도의 최대치 설정
- 1초당 몇 번 리플리케이션을 시도할지 지정한 값
- 기본 값은 100. 즉 이론적으로는 서브는 1/100초 간격으로 리플리케이션을 시도함
- 네트웍 빈도는 최대치일뿐 이를 보장하진 않는다.
- 서버의 Tick Rate에 따라 리플리케이션이 발생하지만, 서버의 성능에 따라 달라짐
- 서버의 성능이 네트웍 빈도보다 낮은 경우, 서버의 성능으로 복제됨
- 일반적으로 그래픽 기능이 없는 데디케이드 서버가 더 좋은 성능을 발휘함
- 네트웍 데이터 줄이기
- 규칙적으로 움직이는 액터의 네트웍 통신 데이터를 줄이는 예제
- NetUpdateFrequency 속성 값을 1로 설정
- 데이터 공백을 클라이언트에서 부드러운 움직임으로 보완하기
- 이전 복제된 데이터에 기반해 현재 틱에서의 회전 값을 예측
- 클라이언트에서 예측된 값을 보간해 회전
- NetUpdateFrequency를 1.0f 값으로 변경하니 클라이언트의 분수대가 뚝뚝 끊어지는 것을 확인할 수 있다.
- 언리얼 인사이트 상으로도 1.0f으로 설정했을때 전송되는 빈도가 적어지는 것을 볼 수 있다. 반면, 100.0f의 경우 거의 매 프레임마다 BP_Fountain이 전송되는것을 볼 수 있다.
// Called every frame
void AABFountain::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 현재는 리슨 서버로 구성되어 있기 때문에 HasAuthority와 같은 체크 함수를 통해 클라이언트와 서버의 로직을 분리시켜줘야한다.
// 데디케이트 서버로 구성되어 있다면 서버로직만 구성하면 된다.
// Server
if (HasAuthority())
{
AddActorLocalRotation(FRotator(0.0f, RotationRate * DeltaTime, 0.0f));
ServerRotationYaw = RootComponent->GetComponentRotation().Yaw;
}
// Client
else
{
// 현재 서버로부터 패킷을 받은 이후의 시간들이 계속 누적
ClientTimeSinceUpdate += DeltaTime;
// 값이 0에 가까우면 return
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);
}
}
// 콜백 함수
// 콜백 함수가 tick보다 더 적게 돌아가는 경우에는 보다 효율적으로 로직을 처리할 수 있다.
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;
}
- ABFountain.cpp
- 빈도는 NetUpdateFrequency = 1.0f으로 설정하지만, 클라이언트에서 보간을 통해 분수대가 자연스럽게 회전하도록 한다.
- 로직은 다음과 같이 구현한다.
- 현재 서버로 받은 회전 값에 다음 패킷이 받아질 것으로 예상 되는 시간에다가 현재 회전, 회전율을 곱해주고, Ratio를 통해 분수대에 Lerp를 시켜주면 된다.
- 기본적인 예제이기 때문에 회전을 통한 Lerp를 구현했지만, 다양하게 클라이언트에서 보간하는 기법을 적절히 섞어 주면 부드럽게 게임을 구현할 수 있다.
- 적응형 네트워크 업데이트(Adaptive Network Update)
- 유의미한 업데이트가 없으면 빈도를 줄여서 부하를 줄이는 기법
- MinNetUpdateFrequency : 리플리케이션 빈도의 최소치 설정을 사용함
- 기본값은 2
- 최소 값과 최대 값 사이에서 현재 액터에 맞는 최적의 전송 타이밍을 설정
- 이를 사용하기 위해서는 설정에서 직접 활성화시켜줘야 함
- DefaultEngine.ini
- [SystemSettings] net.UseAdaptiveNetUpdateFrequency=1
- DefaultEngine.ini
- 연관성(Relevancy)이란?
- 서버의 관점에서 현재 액터가 클라이언트의 커넥션에 관련된 액터인지 확인하는 작업
- 대형 레벨에 존재하는 모든 액터 정보를 클라이언트에게 보내는 것은 불필요함
- 클라이언트와 연관있는 액터만 체계적으로 모아 통신 데이터를 최소화하는 방법
- 연관성에 관련된 다양한 속성
- 연관성 판별을 위한 특별한 액터의 정의
- 뷰어(Viewer) : 클라이언트의 커넥션을 담당하는 플레이어 컨트롤러를 가리킴
- 뷰 타겟(View Target) : 플레이어 컨트롤러가 빙의한 폰
- 가해자(Instigator) : 나에게 데미지를 가한 액터
- 오너(Owner)의 정의
- 액터를 소유하는 액터. 최상단의 소유 액터를 의미
- 연관성 판별을 위한 특별한 액터의 정의
- 연관성의 점검
- 서버에서는 틱마다 모든 커넥션과 액터에 대해 연관성을 점검함
- 클라이언트의 뷰어와 관련있고 뷰어와의 일정 거리 내에 있는 액터를 파악
- 해당 액터 묶음의 정보를 클라이언트에게 전송
- 액터 속성에 따른 연관성 판정을 위한 속성
- AlwaysRelevant : 항상 커넥션에 대해 연관성을 가짐
- 뷰어와 무관하게 항상 복제해야하는 액터들에게 설정. 네트워크 트래픽 up ex) 게임 스테이트, 플레이어 스테이트..
- NetUseOwnerRelevancy : 자신의 연관성은 오너의 연관성으로 판정함
- OnlyRelevantToOwner : 오너에 대해서만 연관성을 가짐
- 뷰어나 뷰 타겟을 오너로 하지 않으면 아예 연관성에서 제외
- NetCullDistance : 뷰어와의 거리에 따라 연관성 여부를 결정함
- AlwaysRelevant : 항상 커넥션에 대해 연관성을 가짐
- 액터, 폰, 플레이어 컨트롤러의 IsRelevantFor 코드 살펴보기
virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const override;
- ABFountain.h
// ...
// 거리에 따른 연관성 파악 변수
NetCullDistanceSquared = 4000000.0f;
- ABFountain.cpp
- Actor 클래스에 있는 IsNetRelevantFor함수를 오버라이드 하고, 생성자에서 NetCullDistanceSquared를 수정해서 확인해보자.
- NetCullDistanceSquared보다 더 멀리 떨어지게 되면 돌아가던 분수대가 멈추게 된다.
해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이어 프레임웍의 이해>
강의를 수강하고 정리한 내용입니다.
이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이 프레임웍의 이해 강의 | 이득우 - 인프런
이득우 | 또 하나의 언리얼 엔진이라고도 불리는 네트웍 멀티플레이어 프레임웍을 학습합니다. 네트웍 멀티플레이어 게임을 제작할 때 반드시 알아야 하는 주요 개념, 내부 동작 원리, 최적화
www.inflearn.com
728x90
'공부 > 이득우의 언리얼 프로그래밍' 카테고리의 다른 글
[Study] Part 3 - RPC 기초 (8/15) (0) | 2024.08.23 |
---|---|
[Study] Part 3 - 액터 리플리케이션 로우레벨 플로우 (7/15) (0) | 2024.08.21 |
[Study] Part 3 - 액터 리플리케이션 기초 (5/15) (0) | 2024.08.15 |
[Study] Part 3 - 액터의 역할과 커넥션 핸드셰이킹 (4/15) (0) | 2024.08.14 |
[Study] Part 3 - 커넥션과 오너십 (3/15) (0) | 2024.08.12 |