기본 프로퍼티 리플리케이션
네이티브 코드로 리플리케이트된 프로퍼티를 처리할 때 알아두면 좋을 꼼수 몇 가지에 대해 이야기해 보고자 합니다.
프로퍼티 리플리케이션 전체에 대한 설명은 이 글의 범위를 약간 벗어나는 것이므로, 여기서는 기본에 대해 간단히 짚어보도록 하겠습니다.
프로퍼티 리플리케이션을 위해서는 몇 가지 작업을 해 줘야 합니다:
프로퍼티가 정의된 액터 클래스의 헤더에서, UPROPERTY 선언에 대한 파라미터 중 하나로 'replicated' 키워드가 있는지 확인해야 합니다:
class ENGINE_API AActor : public UObject
{
UPROPERTY( replicated )
AActor * Owner;
};
액터 클래스 구현에서, GetLifetimeReplicatedProps 함수를 구현해 줘야 합니다:
void AActor::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
DOREPLIFETIME( AActor, Owner );
}
액터의 생성자에 bReplicates 플래그가 True 로 설정되어 있는지 확인합니다:
AActor::AActor( const class FPostConstructInitializeProperties & PCIP ) : Super( PCIP )
{
bReplicates = true;
}
대충 그렇습니다. 멤버 변수 'Owner' 는 이제 연결된 모든 클라이언트의 현재 인스턴싱된 이 액터 유형(이 경우 베이스 액터 클래스)의 모든 사본에 대해 동기화될 것입니다.
조건식 프로퍼티 리플리케이션
프로퍼티가 리플리케이션 등록되고나면, 그 등록을 해제할 수 없습니다 (그래서 수명이란 것이 등장합니다). 그 이유는 가급적 많은 정보를 구워넣기 때문에, 같은 프로퍼티 세트에 대한 다수의 접속에 걸쳐 작업 공유의 이점을 활용할 수 있는 것입니다. 이를 통해 계산 시간이 많이 절약됩니다.
그러면 이 프로퍼티 리플리케이트에 대한 더욱 미세한 제어가 어떻게 가능할까요? 바로 조건식 프로퍼티 입니다.
기본적으로 각 리플리케이트된 프로퍼티에는 내장된 조건이 있는데, 그것은 변경되지 않은 경우 리플리케이트하지 않는 것입니다.
프로퍼티 리플리케이션에 대한 제어를 강화하기 위해, 부차적인 조건을 추가시킬 수 있는 특수 매크로가 있습니다.
이 매크로는 DOREPLIFETIME_CONDITION 이라 불립니다. 그 사용법에 대한 예제는 아래에서 확인할 수 있습니다:
void AActor::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
DOREPLIFETIME_CONDITION( AActor, ReplicatedMovement, COND_SimulatedOnly );
}
조건 매크로에 전달된 'COND_SimulatedOnly' 플래그는 이 프로퍼티의 리플리케이션 고려를 하기도 전에 추가적인 검사를 하도록 합니다. 이 경우, 이 액터의 시뮬레이션 사본이 있는 클라이언트에만 리플리케이트될 것입니다.
이 방법의 큰 장점 중 하나는 대역폭이 절약된다는 점입니다. 이 액터의 autonomous (자율) 프록시 버전을 갖는 클라이언트는 (이 프로퍼티를 예측 목적으로 직접 설정한다든가 해서) 이 프로퍼티에 대해 알 필요가 없다고 결정했기 때문입니다. 다른 장점은 이 프로퍼티를 받지 않는 클라이언트의 경우, 서버는 이 클라이언트의 로컬 사본에 발을 딛지 않을 것입니다.
현재 지원되는 조건 목록을 한 눈에 살펴볼 수 있는 목록입니다:
- COND_InitialOnly - 이 프로퍼티는 초기 번치에만 전송 시도합니다.
- COND_OwnerOnly - 이 프로퍼티는 액터의 오너에게만 전송합니다.
- COND_SkipOwner - 이 프로퍼티는 오너를 제외한 모든 접속에 전송합니다.
- COND_SimulatedOnly - 이 프로퍼티는 simulated 액터에만 전송합니다.
- COND_AutonomousOnly - 이 프로퍼티는 autonomous 액터에만 전송합니다.
- COND_SimulatedOrPhysics - 이 프로퍼티는 simulated 또는 bRepPhysics 액터에 전송합니다.
- COND_InitialOrOwner - 이 프로퍼티는 초기 패킷에서 또는 액터의 오너에 전송합니다.
- COND_Custom - 이 프로퍼티에는 특별한 조건이 없지만, SetCustomIsActiveOverride 를 통해 껐다 켰다 토글 기능이 필요합니다.
지금까지 이미 알려진 상태를 기반으로 한 조건에 대해 이야기해 봤습니다. 프로퍼티 리플리케이션에 대한 충분한 제어가 가능하면서도 엔진에 필수적인 최적화 작업을 하는 것이 쉬워집니다.
그러나 이 정도의 제어로 충분하지 않다면? 이 주제에 대해 이야기할 것이 한 가지 더 있습니다. 원하는 어떠한 조건이든 사용해서 프로퍼티 리플리케이트 여부와 시점에 대해 완벽한 제어가 가능한 DOREPLIFETIME_ACTIVE_OVERRIDE 라는 매크로가 있습니다. 한 가지 주의점은, 접속 단위가 아니라 액터 단위로 작동한다는 점입니다. 즉 다른 말로, 커스텀 조건에서 접속별로 달라질 수 있는 상태를 사용하는 것은 안전하지 않다는 것입니다. 예제는 아래와 같습니다:
void AActor::PreReplication( IRepChangedPropertyTracker & ChangedPropertyTracker )
{
DOREPLIFETIME_ACTIVE_OVERRIDE( AActor, ReplicatedMovement, bReplicateMovement );
}
ReplicatedMovement 프로퍼티는 이제 bReplicateMovement 가 True 일때만 리플리케이트 됩니다.
그러면 이 매크로를 항상 사용하지 않을 이유가 있을까요? 두 가지 있습니다:
- 커스텀 조건 값이 많이 바뀌면 느려질 수 있습니다.
- 접속 단위로 바뀔 수 있는 조건은 사용할 수 없습니다 (여기에는 RemoteRole 체크를 하지 마세요).
프로퍼티 리플리케이션 조건으로 콘트롤과 퍼포먼스 사이의 균형을 찾을 수 있습니다. 엔진이 다수의 접속에 보낼 프로퍼티를 검사하고 보내는 데 걸리는 시간을 최적화시킬 수 있는 기회를 주면서도, 프로그래머에게 프로퍼티 리플리케이트 방법과 시점을 미세하게 조정할 수 있도록 해 주는 것입니다.
지금까지 약간의 네트워킹 꼼수였습니다. 질문이 있으신가요? 포럼에서 이야기 계속 나누시지요. 한글 사용자분들을 위해 네이버 카페도 준비되어 있습니다.