Replication이란?

Unreal Replication이 필요한 이유를 한 문장으로 정리하면, Multiplay Game에서 Server와 Client간의 정보가 동기화되어야 하기 때문이다. Replication을 통해 Server는 연결된 모든 Client들에게 성공적으로 동일한 GameState를 보여줄 수 있다.

Replication에서 Key Concept

  • Server-Client 구조

    혼자서 게임을 하는 경우에는 Player가 임의로 GameStateGameMode에 접근해도 아무런문제가 되지 않는다. 하지만, 멀티플레이의 경우라면 한명의 Player가 자기 맘대로 게임의 규칙이나 진행중인 게임의 상태(점수 현황, Player의 체력, 무기)를 바꿔버리면 큰일이 나기 때문에 이를 관리하는 권한을 Server에게 전적으로 위임한다.

  • Replicated 된 변수 및 Actor

    변수의 UPROPERTY의 tag를 통해 이 변수가 Replicated 될지 말지를 설정할 수 있다. Actor의 경우에는 엑터 클래스->bReplicates = true;를 통해 Actor의 내부 변수, 위치 등을 Replicate 할 수 있다.

  • RPCs(Remote Procedure Call)

    RPC는 Computer Network에서 종종 접했을 수 있는 용어이다. RPC를 사용하면 사실상 remote server에서 함수를 call 하지만, 마치 local에서 call하는 것처럼 손쉽게 Server에 존재하는 함수를 호출할 수 있다. 다른 곳에 존재하는 함수를 RPC로 네트워크에 대한 복잡한 이해없이 간편하게 사용할 수 있다고 생각해보자.

    언리얼에서는 총 3가지의 RPC가 있는데, Client->Server인 Server RPC, Server->Client인 Client RPC, 마지막으로 Server->연결된 모든 Client인 Multicast RPC가 있다.

Server RPC의 예시

UFUNCTION(Server, Reliable, WithValidation)
void ServerTakeDamage(int32 DamageAmount);

위의 코드에서 UFUNCTION 2번째 인자에 Reliable이 들어가게 된다. 기본적인 Unreal은 UDP를 사용해서 데이터를 전송하게 되는데 UDP는 속도가 빠른대신 HTTP에 비해 불안정한 성격을 지닌다. Unreal은 상대적으로 더 안정한 UDP를 자체적으로 만들어서 사용하는데 이를 Reliable UDP라 부르고 UPROPERTY의 Reliable tag를 통해 이것을 사용하겠다는 것이다.

사실 모든 변수들이 Reliable 할 필요는 없는데, 예를 들어서 라이플 총소리처럼 다다다다 울리는 소리를 매 프레임마다 아주 정확하게 전달할 필요는 없다. 하지만 사용자의 인벤토리 정보나 체력 등과 같은 중요 정보는 Reliable UDP를 통해 전송하여야 한다.

* Replication이 필요한 상황

GameState, PlayerState, GameMode에 영향을 주지 않는 변수를 굳이 replicate할 필요가 있을까? 변수마다 존재 이유를 잘 파악해 replicate 할지 말지를 정해보자.

언리얼 주요 클래스 별 Replication

언리얼 주요 클래스들의 Client/Server 동작

언리얼 Dedicated Server를 시작하기에 앞서 가장 먼저 파악하여야 하는 것이 언리얼 주요 클래스들이 Server-Side에서는 어떻게 동작하고 Client-Side에서는 어떻게 동작하는 것이다. 하나 더 추가하자면 언리얼 주요 클래스들의 실행 순서이다.

Client-Side에서 볼때, 위 표에서 있는 항목들에 대해서 PlayerController를 제외하고는 Client에 존재하지 않거나 Server로부터 Replicated 된 정보를 받아 ReadOnly인 경우들이다. 다시 한번, Server가 얼마나 강력한 권한을 가진 지 알 수 있다.

Client-Side, Server-Side에 생성되는 언리얼 클래스

RepNotify

AnimNotify가 Animation Sequence에서 특정 부분에 다다르면 수행될 함수였다는 것을 상기해보자. RepNotify(Replication Notify) 는Replication 처리된 변수가 변경되면 RepNotify로 설정된 함수가 자동으로 호출된다. Delegate와 유사한 느낌이라고 생각하면 된다.

RepNotify 사용하는 방법

  1. 변수를 선언한다. UPROPERTY 안에 ReplicatedUsing라는 MetaData를 세팅하게 되는데 여기에 해당 변수가 업데이트 될때 호출되는 함수의 이름을 적는다. UPROPERTY MetaData에 따로 Replicated 라고 적지 않아도 ReplicatedUsing라고 적으면 자동으로 Replicate한다.
UPROPERTY(ReplicatedUsing = OnRep_PlayerHealth)
int32 PlayerHealth;
  1. 헤더 파일에 함수 선언. OnRep 접두사를 붙여서 함수 이름을 정해준다.
UFUNCTION()
void OnRep_PlayerHealth();
  1. OnRep 함수의 구현
void AMyPlayerCharacter::OnRep_PlayerHealth()
{
    // PlayerHealth 변수가 업데이트 되면 실행 
    UpdateHealthBar(PlayerHealth);
}
  1. GetLifetimeReplicatedProps 함수의 정의. Replicate가 될 변수들은 모조리 (해당 Class - 변수 이름)의 형태로 이 함수 안에 정의되어야 한다.
void AMyPlayerCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    // DOREPLIFETIME 함수의 인자로 (해당 Class - 변수 이름)을 넣는다.
    DOREPLIFETIME(AMyPlayerCharacter, PlayerHealth);
}
  1. Server에서 해당 변수를 업데이트한다.
void AMyPlayerCharacter::TakeDamage(int32 DamageAmount)
{
    if (HasAuthority())  // Ensure this is executed on the server
    {
        PlayerHealth -= DamageAmount;
        // If health falls below zero, the player dies
        if (PlayerHealth <= 0)
        {
            Die();
        }
    }
}
profile
🏦KAIST EE | 🏦SNU AI(빅데이터 핀테크 전문가 과정) | 📙CryptoHipsters 저자

0개의 댓글