Variable Replication

정재훈·2023년 10월 16일
0

Unreal Multiplay

목록 보기
6/7

이전글(Network Role)

언리얼 multiplay는 Authorative server model을 사용하기 때문에
서버는 게임 세계의 상태를 관리하고 갱신하며 클라이언트는 이 정보를 서버와 동기화해준다.
이때 변수를 서버에서 클라이언트로 복제하여 동기화하는 방식이 variable replication 이다.

예시)

Weapon class

멀티플래이 fps게임에서 캐릭터가 바닥에 떨어져있는 weapon과 overlap 될때
pickup widget을 보여주는 코드다.

  • Weapon.cpp
void AWeapon::BeginPlay()
{
	Super::BeginPlay();

	if (HasAuthority())
	{
		AreaSphere->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
		AreaSphere->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
		AreaSphere->OnComponentBeginOverlap.AddDynamic(this, &AWeapon::OnSphereOverlap);
	}
	if (PickupWidget)
	{
		PickupWidget->SetVisibility(false);
	}
}

먼저 weapon class를 보면 땅에 떨어진 weapon은 주인인 character class가 없기 때문에
character가 overlap되는 부분은 서버에서 관리해주는게 합리적이다.
따라서 HasAuthority (Local Role에서 ENetRole::ROLE_Authority을 의미한다 따라서 서버다)
를 조건문으로 체크해 서버에서 overlap관련 코드를 다룬다.

pickup widget은 디폴트로 안보이게 설정해둔다.

  • Weapon.cpp
void AWeapon::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(OtherActor);
	if (BlasterCharacter && PickupWidget)
	{
		PickupWidget->SetVisibility(true);
	}
}

overlap이 일어난 character가 있을때 pickup widget을 보이게 설정한다.

문제점

위 코드대로 실행했을경우
서버에서는 서버의 캐릭터가 무기와 overlap 되었을때
화면에 pickup widget을 정상적으로 보여주지만
client 캐릭터가 무기와 overlap되었을때 문제가 생긴다.

위 사진을 보면 오른쪽 아래 화면 client가 무기에 다가갔지만 pickup widget을 보여주지 않고
무기에 다가가지 않은 서버의 화면에서 pickup widget이 보인다.
overlap에 관한 함수를 서버에서만 실행해 생긴일인데 이 문제를 해결하기 위해 variable replication이 사용된다.

Variable Replication

  • BlasterCharacter.h
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

UPROPERTY(ReplicatedUsing = OnRep_OverlappingWeapon)
	class AWeapon* OverlappingWeapon;
  • BlasterCharacter.cpp
void ABlasterCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME_CONDITION(ABlasterCharacter, OverlappingWeapon, COND_OwnerOnly);
}

복제될 변수는 선언할때 UPROPERTY에 Replicated 키워드를 사용해야한다.

  • UPROPERTY(Replicated) : 변수의 값이 바뀌면 서버에서 클라이언트로 값을 복제한다.
  • UPROPERTY(ReplicatedUsing = 함수명) : 값이 복제될때 등록한 함수를 호출한다.

또한 GetLifetimeReplicatedProps함수를 override해 변수를 등록해야한다.
DOREPLIFETIME_CONDITION(클래스, 변수, 복제 조건) 매크로를 통해 변수를 복제하는데 유의해야할점은 복제 조건이다.

복제조건

  • COND_InitialOnly
    이 프로퍼티는 초기 번치에만 전송을 시도합니다.

  • COND_OwnerOnly
    이 프로퍼티는 액터의 오너에만 전송합니다.

  • COND_SkipOwner
    이 프로퍼티는 오너를 제외한 모든 접속에 전송합니다.

  • COND_SimulatedOnly
    이 프로퍼티는 시뮬레이션되는 액터에만 전송합니다.

  • COND_AutonomousOnly
    이 프로퍼티는 자율 액터에만 전송합니다.

  • COND_SimulatedOrPhysics
    이 프로퍼티는 시뮬레이션되는 또는 bRepPhysics 액터에 전송합니다.

  • COND_InitialOrOwner
    이 프로퍼티는 초기 패킷시, 또는 액터의 오너에 전송합니다.

  • COND_Custom
    이 프로퍼티에는 별다른 조건이 없지만, SetCustomIsActiveOverride 를 통해 껐다 켰다 토글 기능을 원합니다.

이 프로그램에서는 weapon이 overlap되는 character 오너에만 복제할 것 이므로
COND_OwnerOnly를 사용하는 것이 맞다.

  • BlasterCharacter.cpp
void ABlasterCharacter::SetOverlappingWeapon(AWeapon* Weapon)
{
	Super::Tick(DeltaTime);
	if (OverlappingWeapon)
	{
		OverlappingWeapon->ShowPickupWidget(false);
	}
    
    //change value
	OverlappingWeapon = Weapon;
    
    //for server
	if (IsLocallyControlled())
	{
		if (OverlappingWeapon)
		{
			OverlappingWeapon->ShowPickupWidget(true);
		}
	}
}
  • Weapon.cpp
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);
	}
}
  • BlasterCharacter.cpp
void ABlasterCharacter::OnRep_OverlappingWeapon(AWeapon* LastWeapon)
{
	if (OverlappingWeapon)
	{
		OverlappingWeapon->ShowPickupWidget(true);
	}
	if (LastWeapon)
	{
		LastWeapon->ShowPickupWidget(false);
	}
}

character class에 overlap되고있는 변수를 선언해 줬으니
SetOverlappingWeapon 함수를 만들어주고 Weapon class에서 overlap이 발생하면
해당 character에 함수를 호출해 변수값을 변경해준다.
변수값이 변경되면 client에서 OnRep_OverlappingWeapon함수가 호출되어 weapon class에 ShowPickupWidget을 호출해 client 화면에서 widget을 보여주게 된다.

주의점

Weapon class에서 overlap은 서버에서만 관리해왔으므로
Character class에 SetOverlappingWeapon함수는 서버에서 실행되는 경우밖에 없으며
OnRep_OverlappingWeapon함수는 서버에서 client에 변수값이 복제될때 client에서 호출되는 함수이므로
client에서 실행되는 경우밖에 없다.

따라서 client character가 overlap해 widget을 보여주는 경우는 OnRep_OverlappingWeapon함수에서 다루고
서버 character가 overlap하는경우는 SetOverlappingWeapon 함수에서 IsLocallyControlled() 서버가 컨트롤 하는 캐릭터인지 확인하고 widget을 보여준다.
(만약 IsLocallyControlled를 확인안하면 서버의 화면에서는 다른 캐릭터가 overlap해도 widget을 보여주게된다)

profile
게임 개발 공부중!

0개의 댓글