728x90
반응형
- 정리
- RPC 기초
- RPC 함수를 선언하는 방법과 기본 사용 방법의 이해
- RPC 종류에 따른 동작 방식의 이해
- RPC 사용시 주의할 점의 확인
- 프로퍼티 리플리케이션과 NetMulticast RPC의 차이점 이해
- RPC 기초
- RPC(Remote Procedure Call)이란?
- 원격 프로시저(함수) 호출의 약자
- 원격 컴퓨터에 있는 함수를 호출할 수 있도록 만든 통신 프로토콜
- 네트웍 멀티플레이에서 서버와 클라이언트간에 빠르게 행동을 명령하고 정보를 주고받는데 사용
- 언리얼 엔진에서 클라이언트에서 서버로 통신하는 유일한 수단을 제공
- Client RPC 개요
- 서버에서 클라이언트로 호출하는 RPC
- 이를 활용해 특정 클라이언트에게만 명령을 보낼 수 있음
- 서버에서 명령을 보낼 클라이언트의 커넥션을 소유한 액터를 사용해야 함 (AActor::GetNetConnection)
- NetMulticast RPC 개요
- 서버를 포함해 모든 플레이어에게 명령을 보내는 RPC
- 프로퍼티 리플리케이션과 유사하게 연관성 기반으로 동작함 (커넥션을 소유하지 않아도 동작)
- 프로퍼티 리플리케이션과 유사하지만 다른 용도로 사용
- RPC 선언에 관련된 키워드 정리
- Unreliable : RPC 호출을 보장하지 않는 옵션. 빠름
- Reliable : RPC 호출을 보장해주는 추가 옵션. 정말 필요한 때만 호출
- WithValidation : 서버에서 검증 로직을 추가로 구현할 때 추가하는 옵션
UFUNCTION(NetMulticast,Unreliable)
void MulticastRPCChangeLightColor(const FLinearColor& NewLightColor);
- ABFountain.h
void AABFountain::MulticastRPCChangeLightColor_Implementation(const FLinearColor& NewLightColor)
{
AB_LOG(LogABNetwork, Log, TEXT("LightColor : %s"), *NewLightColor.ToString());
UPointLightComponent* PointLight = Cast<UPointLightComponent>(GetComponentByClass(UPointLightComponent::StaticClass()));
if (PointLight)
{
PointLight->SetLightColor(NewLightColor);
}
}
- ABFountain.cpp
- Multicast RPC 함수를 통한 테스트
- 액터와 가까이 있을 때는 연관성에 관련되어 정상적으로 RPC가 2번씩(서버, 클라이언트) 호출되는 것을 볼 수 있고, 액터와 멀어졌을 경우 서버에서만 RPC가 호출되는 것을 볼 수있다.
// MulticastRPC
UFUNCTION(NetMulticast,Unreliable)
void MulticastRPCChangeLightColor(const FLinearColor& NewLightColor);
// WithValidation - 검증기능
// ServerRPC
UFUNCTION(Server, Unreliable, WithValidation)
void ServerRPCChangeLightColor();
// ClientRPC
UFUNCTION(Client, Unreliable)
void ClientRPCChangeLightColor(const FLinearColor& NewLightColor);
- ABFountain.h
// Called when the game starts or when spawned
void AABFountain::BeginPlay()
{
Super::BeginPlay();
// 서버에 전달(HasAuthority)
if (HasAuthority())
{
FTimerHandle Handle;
GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([&]
{
const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
ClientRPCChangeLightColor(NewLightColor);
}
), 1.0f, true, 0.0f);
FTimerHandle Handle2;
GetWorld()->GetTimerManager().SetTimer(Handle2, FTimerDelegate::CreateLambda([&]
{
// 반복문의 다른 방법
// 모든 액터의 타입에 대해서 쉽게 목록을 가져올 수 있는 기능
for (APlayerController* PlayerController : TActorRange<APlayerController>(GetWorld()))
{
if (PlayerController && !PlayerController->IsLocalPlayerController())
{
SetOwner(PlayerController);
break;
}
}
}
), 10.0f, false, -1.0f);
// 오너십을 확인하는 함수
GetNetConnection();
}
// 클라이언트 일 경우에만
else
{
}
}
// ...
// ClientRPC
void AABFountain::ClientRPCChangeLightColor_Implementation(const FLinearColor& NewLightColor)
{
AB_LOG(LogABNetwork, Log, TEXT("LightColor : %s"), *NewLightColor.ToString());
UPointLightComponent* PointLight = Cast<UPointLightComponent>(GetComponentByClass(UPointLightComponent::StaticClass()));
if (PointLight)
{
PointLight->SetLightColor(NewLightColor);
}
}
// WithValidation 기능을 추가하기 위해서 필요한 함수
bool AABFountain::ServerRPCChangeLightColor_Validate()
{
// true = 검증 성공
// false = 검증 실패
// 검증에 실패하면 클라이언트가 즉각적으로 차단이 되고 해당 클라이언트는 스탠드얼론으로 돌아가게 된다.
return true;
}
// Server RPC - 클라이언트가 서버에 명령을 내리는 유일한 방법
// 클라이언트가 서버에게 LightColor를 변경하라고 명령을 내림, 서버는 모든 클라이어은트와 자기 자신에서 MulticastRPC로 LightColor를 변경하라고 명령을 내림
void AABFountain::ServerRPCChangeLightColor_Implementation()
{
const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
MulticastRPCChangeLightColor(NewLightColor);
}
// Multicast RPC
void AABFountain::MulticastRPCChangeLightColor_Implementation(const FLinearColor& NewLightColor)
{
AB_LOG(LogABNetwork, Log, TEXT("LightColor : %s"), *NewLightColor.ToString());
UPointLightComponent* PointLight = Cast<UPointLightComponent>(GetComponentByClass(UPointLightComponent::StaticClass()));
if (PointLight)
{
PointLight->SetLightColor(NewLightColor);
}
}
- ABFountain.cpp
- Unreliable RPC
- 목적지에 도착한다는 보장은 없지만, 더 빠르고 더 자주 전송할 수 있음. 게임플레이에 중요하지 않거나 매우 자주 호출되는 함수에 가장 적합
- 예를 들어, 액터의 움직임은 매 프레임마다 변경될 수 있기 때문에 신뢰할 수 없는 RPC를 사용하여 리플리케이트된다.
- Reliable RPC
- 목적지에 도착할 수 있도록 보장되며, 게임플레이에 중요하지만 자주 호출되지 않는 함수에 가장 적합
- 예를 들어, 충돌 이벤트, 무기 발사 시작 또는 종료, 액터 스폰 등이 이에 해당한다.
- RPC 사용시 주의할 점 정리
- 각 RPC 종류마다 올바르게 사용할 것
- Client, NetMulticast는 서버에서만 호출
- Server는 클라이언트에서 호출하지만, 플레이어로 참여하는 리슨서버의 경우 호출 가능
- Client, Server는 오너십을 가지고 있는 액터에서 호출
- 컨트롤러 : IsLocalController 함수
- 폰 : IsLocallyControlled 함수
- Tick 및 빈번하게 호출되는 함수에 Reliable RPC를 사용하지 말 것
- NetMulticast RPC의 잦은 사용은 네트웍 부하를 가중시키니 신중을 기할 것
- 게임 플레이 및 액터 상태에 영향을 미치는 경우 RPC보다 프로퍼티 리플리케이션을 사용할 것
- 각 RPC 종류마다 올바르게 사용할 것
- Property Replication vs NetMulticast RPC
- 유사점
- 서버와 모든 클라이언트의 지정한 함수를 호출할 수 있음
- 지정한 데이터 전송을 보장할 수 있음
- 액터의 오너십과 무관하게 연관성으로 동작함
- 차이점
- Property Replication으로 설정한 데이터는 클라이언트에 반드시 동기화됨
(RPC 전송의 Reliability와 다른 개념) - NetMulticast RPC를 호출한 타이밍에 클라이언트가 없으면 해당 데이터를 받을 길이 없음
- Property Replication으로 설정한 데이터는 클라이언트에 반드시 동기화됨
- 정리
- Property Replication은 게임에 영향을 미치는 데이터에 사용 (Gameplay Property)
- NetMulticast RPC는 게임과 무관한 휘발성 데이터에 사용 (Cosmetic)
- 유사점
- Property Replication vs NetMulticast RPC 실습해보기
- 실행후 5초뒤에 Property Replication이 호출되는 코드와 NetMulticast RPC가 호출되는 코드를 비교해보자.
FTimerHandle Handle3;
GetWorld()->GetTimerManager().SetTimer(Handle3, FTimerDelegate::CreateLambda([&]
{
ServerLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
// 프로퍼티 리플리케이션
OnRep_ServerLightColor();
// Multicast RPC
/*const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
MulticastRPCChangeLightColor(NewLightColor);*/
}
), 5.0f, false, -1.0f);
- ABFountain.cpp
- 서버를 실행하고 5초뒤에 분수대의 색이 변경되었고, 이후 클라이언트를 실행했을때 정상적으로 분수대의 변경된 색이 적용된 것을 볼 수 있다.
- 클라이언트의 분수대는 프로퍼티 리플리케이션에 의해 PostNetInit 함수를 통해서 변경된 기본 속성값들을 모두 받게 된다.
- 최근에 가장 마지막에 서버에서 변경된 프로퍼티 내용이 전송되면서 클라이언트에 반영이 된다.
FTimerHandle Handle3;
GetWorld()->GetTimerManager().SetTimer(Handle3, FTimerDelegate::CreateLambda([&]
{
//ServerLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
//// 프로퍼티 리플리케이션
//OnRep_ServerLightColor();
// Multicast RPC
const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
MulticastRPCChangeLightColor(NewLightColor);
}
), 5.0f, false, -1.0f);
- ABFountain.cpp
- 서버를 실행하고 5초뒤에 분수대의 색이 변경되었고, 이후 클라이언트를 실행했을때 클라이언트의 분수대는 초기에 설정된 변경되지 않은 색으로 나타나게 된다.
- 이미 RPC로 호출했을 때의 타이밍을 지났기 때문에 클라이언트는 바뀐 분수대의 색상을 받아올 길이 없다. 이미 데이터는 휘발되었다.
- 게임 플레이에 영향을 미치는 중요한 데이터들은 Property Replication을 사용하고, 휘발성으로 동작하는 데이터들은 Multicast RPC를 사용하도록 하자.
해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이어 프레임웍의 이해>
강의를 수강하고 정리한 내용입니다.
이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이 프레임웍의 이해 강의 | 이득우 - 인프런
이득우 | 또 하나의 언리얼 엔진이라고도 불리는 네트웍 멀티플레이어 프레임웍을 학습합니다. 네트웍 멀티플레이어 게임을 제작할 때 반드시 알아야 하는 주요 개념, 내부 동작 원리, 최적화
www.inflearn.com
728x90
'공부 > 이득우의 언리얼 프로그래밍' 카테고리의 다른 글
[Study] Part 3 - 캐릭터 공격 구현 개선 (10/15) (0) | 2024.08.27 |
---|---|
[Study] Part 3 - 캐릭터 공격 구현 (9/15) (0) | 2024.08.26 |
[Study] Part 3 - 액터 리플리케이션 로우레벨 플로우 (7/15) (0) | 2024.08.21 |
[Study] Part 3 - 액터 리플리케이션 빈도와 연관성 (6/15) (0) | 2024.08.20 |
[Study] Part 3 - 액터 리플리케이션 기초 (5/15) (0) | 2024.08.15 |