멀티플레이어 게임에서 특수한 판정은 모두 서버에서 계산된 다음, 클라이언트에게 Replicate되어야 한다. 이 Replicate는 코드상에서 어떻게 구현되어야 하는지 알아보자.
현재 땅에 떨어진 무기를 픽업을 위해서 무기의 SphereComponent에 Overlap되었을 때, 위젯 컴포넌트를 표시해야 한다. 여기서 Overlap 판정은 서버에서 실행되어야 한다. 현재 ENetRole을 검사해서 ROLE_Authority인 경우에만, Overlap 델리게이트를 바인드한다.
AWeapon::AWeapon()
{
bReplicates = true; // replcate가 필요하므로(AreaSphere 판정) true로 설정한다.
}
void AWeapon::BeginPlay()
{
Super::BeginPlay();
// Authority가 있다면 == 현재 ENetRole가 ROLE_Authority라면 == 서버라면
// 콜리전을 활성화해서 오버랩을 판정한다.
if (HasAuthority())
{
AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
AreaSphere->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AWeapon::OnSphereOverlap);
AreaSphere->OnComponentEndOverlap.AddDynamic(this, &AWeapon::OnSphereEndOverlap);
}
if (PickupWidget)
{
PickupWidget->SetVisibility(false);
}
}
void AWeapon::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
if (BlasterCharacter)
{
BlasterCharacter->SetOverlappingWeapon(this);
}
}
void AWeapon::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
if (BlasterCharacter)
{
BlasterCharacter->SetOverlappingWeapon(nullptr);
}
}
게임에서 컨트롤하는 캐릭터의 클래스에 현재 Overlap된 Weapon을 표현하는 멤버변수인 OverlappingWeapon이 있다. OverlappingWeapon은 위의 코드에서 SphereCollision과 Overlap되었을 때, 해당 Weapon이 저장된다. 이는 서버에서만 이루어지기 때문에, 이 변수를 Replicate해주어야 한다.
private:
// OverlappingWeapon이 Replicate될 때마다 자동적으로 호출되는 함수이다.
// OnRep_는 Replicate될 때마다 호출된다. == Replicate는 서버에서 클라이언트 원웨이이므로 서버에서는 절대 호출되지 않는다.
// OnRep_는 하나의 매개변수를 추가할 수 있는데, Replicate되기 전의 해당 변수를 받을 수 있다.
UFUNCTION()
void OnRep_OverlappingWeapon(AWeapon* LastWeapon);
// 오버랩된 무기가 서버에서 변경되었을 경우, 다른 클라이언트에도 알려야 한다.
// UPROPERTY(Replicated)를 붙이면 OverlappingWeapon은 Replicated variable이 된다.
// 추가적으로 ReplicatedUsing을 사용하면 변경될 때마다 자동적으로 호출되는 함수를 설정할 수 있다.
UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
AWeapon* OverlappingWeapon;
UPROPERTY(Replicated)나 UPROPERTY(ReplicatedUsing = OnRep_...)로 UPROPERTY를 설정해주면 해당 변수는 Replicated variable로 사용할 수 있다. ReplicatedUsing을 사용하면 해당 변수가 Replicate되었을 때, 자동적으로 호출되는 함수를 지정해 줄 수 있다. 이렇게 Replicated variable을 사용하려면 GetLifetimeReplicatedProps함수를 재정의해서 해당 변수를 등록해주어야만 한다.
void ABlasterCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// OverlappingWeapon은 서버에서 Overlap판정이 실행된다.
// 판정된 OverlppingWeapon은 서버에서 클라이언트에게 Replicate되어야 한다.
// 밑은 그 등록을 의미하며, COND_OwerOnly이므로 Overlap된 주인에게만 Replicate된다.
DOREPLIFETIME_CONDITION(ABlasterCharacter, OverlappingWeapon, COND_OwnerOnly);
}
여기서 주목해야 할 점은, COND_OwerOnly를 매크로에 추가적으로 입력하는데, 이는 Overlap된 주인 클라이언트에게만 Replicate된다는 점이다.
무기가 Overlap되었을 때, this를 매개변수로 캐릭터의 SetOverlappingWeapon함수가 호출된다.
void ABlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon)
{
// IsLocallyControlled는 현재 컨트롤하고 있는 폰인지 확인한다.
// == overlap 판정과 SetOverlappingWeapon함수는 서버에서 이루어진다. 즉, 서버가 컨트롤하는 폰인지 확인한다.
if (IsLocallyControlled())
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(false);
}
}
OverlappingWeapon = Weapon;
// IsLocallyControlled는 현재 컨트롤하고 있는 폰인지 확인한다.
// == overlap 판정과 SetOverlappingWeapon함수는 서버에서 이루어진다. 즉, 서버가 컨트롤하는 폰인지 확인한다.
if (IsLocallyControlled())
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
}
}
이 함수는 OverlappingWeapon을 넘겨받은 AWeapon객체로 변경한다. OverlappingWeapon이 서버에서 변경되었기에, Replicate되어 OnRep_OverlappingWeapon이 호출된다. 서버에서는 Replicate가 동작되지 않기에 OnRep_OverlappingWeapon이 호출되지 않으므로, 이 함수에서 위젯을 표시/비활성화한다.
// 클라이언트에서 위젯이 표시되도록 한다.
void ABlasterCharacter::OnRep_OverlappingWeapon(AWeapon* LastWeapon)
{
if (OverlappingWeapon)
{
OverlappingWeapon->ShowPickupWidget(true);
}
// OverlappedWeapon이 바뀌고(Replicate되고) 이전에 OverlappedWeapon의 위젯은 끈다.
if (LastWeapon)
{
LastWeapon->ShowPickupWidget(false);
}
}
OnRep_OverlappingWeapon에서 Overlap된 AWeapon의 위젯 컴포넌트를 표시한다. OnRep_함수는 하나의 매개변수를 추가할 수 있는데 Replicate되기 전의 해당 변수를 받을 수 있다. 이를 통해 Overlap이 끝났을 때, 위젯 컴포넌트를 다시 비활성화 시킬 수 있다.
'Unreal Engine > 기타' 카테고리의 다른 글
[UE] HUD와 PlayerController (0) | 2024.01.14 |
---|---|
[UE] RPC : Remote Procedure Calls (0) | 2023.12.21 |
[UE] Network Role (1) | 2023.12.17 |
[UE] ServerTravel과 ClientTravel (0) | 2023.12.08 |
[UE] ThisClass (0) | 2023.12.02 |