RPC 실습

김혁·2025년 8월 29일

예시를 통한 RPC 구현

시나리오

  1. 플레이어가 키 입력을 통해 지뢰 아이템을 생성
  2. 플레이어가 지뢰 아이템을 밟을 시에 아이템이 동작함
  3. 아이템은 동작 시에 폭발 효과 발생

플레이어가 지뢰 아이템을 생성

  • 플레이어가 생성한 지뢰 아이템이 다른 사람들에게 보이게 하려면
    -> Server에서 액터를 스폰시키고, 해당 액터를 클라이언트들에게 복제(액터는 레플리케이션 필수)
    -> Server에게 액터를 스폰시켜달라고 ServerRPC를 통해 요청
// DXPlayerCharacter.h

class ADXPlayerCharacter : public ADXCharacterBase
{
	...
    
    UFUNCTION(Server, Reliable, WithValidation)
    void ServerRPCSpawnMine();
    
    ...
}
// DXPlayerCharacter.cpp

void ADXPlayerCharacter::HandleMineInput(const FInputActionValue& InValue)
{
	if (IsLocallyControlled())
    {
    	ServerRPCSpawnMine();
    }
}

void ADXPlayerCharacter::ServerRPCSpawnMine_Implementation()
{
	if (IsValid(MineClass))
    {
    	ADXMine* SpawnedMine = GetWorld()->SpawnActor<ADXMine>(MineClass, SpawnLocation, SpawnRotation);
    }
}

void ADXPlayerCharacter::ServerRPCSpawnMine_Validate()
{
	return true;
}

지뢰가 작동하면서 폭발 이펙트를 재생

  • 폭발 이펙트는 모든 클라이언트에서 재생되어야 한다.
    -> 지뢰 작동 시에 NetMulticast RPC를 통해 모든 클라이언트에서 이펙트 재생 함수 실행
    -> 이때 지뢰 폭발 데미지는 서버에서만 처리
// DXMine.h

class ADXMine : public AActor
{
	...
    
    UFUNCTION()
    void OnMineBeginOverlap(AActor* OverlappedActor, AActor* OtherActor);
    
    UFUNCTION()
    void MulticastRPCSpawnEffect();
    
    ...
}
// DXMine.cpp

void ADXMine::BeginPlay()
{
	if (!OnActorBeginOverlap.IsAlreadyBound(this, &ThisClass::OnMineBeginOverlap))
    {
    	OnActorBeginOverlap.AddDynamic(this, &ThisClass::OnMineBeginOverlap);
    }
}

void ADXMine::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	if (OnActorBeginOverlap.IsAlreadyBound(this, &ThisClass::OnMineBeginOverlap))
    {
    	OnActorBeginOverlap.RemoveDynamic(this, &ThisClass::OnMineBeginOverlap);
    }
}

void ADXMine::OnMineBeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
	if (HasAuthority())
    {
    	MulticastRPCSpawnEffect();
    }
}

void ADXMine::MulticastRPCSpawnEffect_Implementation()
{
	if (!HasAuthority())
    {
    	Particle->Activate(true);
    }
}

폭발한 지뢰가 다시 폭발하지 않게

  • 위의 시나리오까지 코드를 작성하게 되면 밟을 시에 무한하게 터짐
    -> 폭발한 지뢰가 다시 폭발하지 않게끔 하고 싶음
    -> bIsExploded 변수를 통해서 폭발 여부를 확인하고 터지게 함
    -> 해당 방법은 NetCullDistance 밖에 있거나, 새로 추가된 플레이어는 해당 사실을 모르기 때문에 또 폭발함
  • 해당 변수를 Property Replication을 통해서 동기화시킴
// DXMine.h

class ADXMine : public AActor
{
	...
    
    virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
    
    UPROPERTY(Replicated)
    uint8 bIsExploded : 1;
    
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
    float NetCullDistance;
    
    ...
}
// DXMine.cpp

void ADXMine::GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) 
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    
    DOREFLIFETIME(ThisClass, bIsExploded);
}

void ADXMine::MulticastRPCSpawnEffect_Implementation()
{
	if (HasAuthority())
    {
    	bIsExploded = true;
        return;
    }
    
    if (bIsExploded)
    {
    	return;
    }
    
    Particle->Activate(true);
}

  • Property Replication을 적용해도 폭발이 발생하지 않음
    • 클라이언트로 Property Replication이 되는 시점이 너무 빠름
    • NetMulticastRPC가 실행되기 전에 이미 bIsExploded가 true가 되서 작동이 안 됨
            서버                           클라
MulticastRPCSpawnEffect 실행
     bIsExploded = true
                                    bIsExploded = true
                               MulticastRPCSpawnEffect 실행
  • 따라서 충돌 검사 자체는 서버, 클라이언트 둘 다 하기 때문에 충돌할 때, 바로 로직을 실행
// DXMine.cpp

void ADXMine::OnMineBeginOverlap(AActor* OverlappedActor, AActor* OtherActor)
{
	// 서버에서 폭발 여부 변경
	if (HasAuthority())
    {
    	if (!bIsExploded)
        {
        	bIsExploded = true;
        }
    }
    // 클라이언트에서는 바로 이펙트 재생
    else
    {
    	if (!bIsExploded)
        {
        	Particle->Activate(true);
        }
    }
}


출처 : 팀스파르타 내일배움캠프
profile
게임 개발자를 향해..

0개의 댓글