언리얼 액터 복제 최적화: Update Frequency, Relevancy, Priority, Dormancy
bReplicates를 활성화했다고 해서 서버가 액터의 모든 상태를 매 프레임, 모든 클라이언트에 전송하는 것은 아니다. 실제 복제는 서버가 관리하는 스케줄링 과정 안에서 이루어진다.
서버는 먼저 액터가 이번 업데이트에 참여할 수 있는지 확인하고, 연결마다 해당 액터가 필요한지 판단한 뒤, 제한된 대역폭 안에서 어떤 액터를 먼저 보낼지 결정한다. 마지막으로 이전에 보낸 상태와 비교해 변경된 프로퍼티만 직렬화한다.
복제 가능한 액터
→ Dormancy 및 Update Frequency 검사
→ 연결별 Relevancy 검사
→ Priority에 따른 전송 순서 결정
→ 변경된 프로퍼티 직렬화
→ 클라이언트 반영 및 RepNotify 호출
따라서 네트워크 최적화는 단순히 프로퍼티의 수를 줄이는 작업이 아니다. 언제 검사할지, 누구에게 보낼지, 무엇을 먼저 보낼지, 언제 검사 대상에서 제외할지를 함께 설계해야 한다.
레벨 배치 액터와 런타임 스폰 액터의 복제
NetLoadOnClient는 레벨에 미리 배치된 액터를 클라이언트가 레벨 로딩 과정에서 생성할 수 있는지를 결정한다. 서버가 플레이 도중 SpawnActor()로 생성한 동적 액터의 전달 여부를 정하는 옵션은 아니다.
| 구분 | 레벨 배치 액터 | 런타임 스폰 액터 |
| 생성 시점 | 맵 로딩 과정 | 게임 실행 중 서버가 생성 |
| 클라이언트 생성에 관여하는 설정 | bNetLoadOnClient |
bReplicates와 서버 권한 스폰 |
| 대표 사례 | 문, 상자, 레벨 장치 | 투사체, 드롭 아이템, 동적 NPC |
레벨에 상자를 배치하고 그 회전값을 서버가 관리한다면 액터 복제와 프로퍼티 복제를 모두 활성화해야 한다.
ADXBox::ADXBox()
{
bReplicates = true;
bNetLoadOnClient = true;
}
bNetLoadOnClient는 “클라이언트가 액터를 가질 수 있는가”에 가깝고, 해당 액터의 런타임 상태를 계속 동기화하는 것은 프로퍼티 복제의 역할이다.
Property Replication은 최신 상태를 동기화한다
복제할 프로퍼티는 UPROPERTY(Replicated) 또는 UPROPERTY(ReplicatedUsing=...)로 선언하고, GetLifetimeReplicatedProps()에서 등록한다.
// DXBox.h
UPROPERTY(ReplicatedUsing = OnRep_ServerRotationYaw)
float ServerRotationYaw = 0.0f;
UFUNCTION()
void OnRep_ServerRotationYaw();
// DXBox.cpp
void ADXBox::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps
) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ThisClass, ServerRotationYaw);
}
서버는 권위 있는 값을 변경하고, 클라이언트는 복제된 값이 도착했을 때 OnRep_ServerRotationYaw()에서 표시 상태를 갱신한다.
void ADXBox::OnRep_ServerRotationYaw()
{
SetActorRotation(FRotator(0.0f, ServerRotationYaw, 0.0f));
}
이 방식은 클라이언트 Tick()에서 매 프레임 값의 변화를 감시하는 것보다 의도가 명확하다. 네트워크 값이 실제로 갱신된 시점에만 반응하므로 불필요한 폴링도 피할 수 있다.
다만 Property Replication은 값이 거쳐 간 모든 단계를 이벤트처럼 보장하지 않는다. 서버에서 같은 프로퍼티가 짧은 시간 동안 여러 번 변경되면 클라이언트에는 중간값이 생략되고 최신값만 도착할 수 있다.
| 전달하려는 정보 | 적합한 방식 | 이유 |
| 현재 체력, 점수, 남은 횟수 | Property Replication | 늦게 접속하거나 갱신을 놓친 클라이언트도 최신 상태가 중요하다. |
| 피격 연출, 알림, 일회성 명령 | RPC | 같은 내용이 연속으로 발생하더라도 각각의 호출 자체가 의미를 가진다. |
또한 C++의 RepNotify를 서버 측 게임 로직까지 자동으로 실행해 주는 장치로 생각하면 안 된다. 서버와 클라이언트가 동일한 표현 로직을 사용해야 한다면 공통 함수를 분리하고, 서버는 값을 변경한 뒤 그 함수를 직접 호출하는 구조가 안전하다.
void ADXBox::SetRotationYawOnServer(float NewYaw)
{
check(HasAuthority());
ServerRotationYaw = NewYaw;
ApplyRotationYaw();
}
void ADXBox::OnRep_ServerRotationYaw()
{
ApplyRotationYaw();
}
NetUpdateFrequency: 복제 검사 주기의 상한
NetUpdateFrequency는 서버가 액터를 초당 몇 번까지 복제 대상으로 고려할지를 나타낸다. 예를 들어 값이 10.0f라면 이상적인 검사 간격은 약 0.1초다.
복제 검사 간격 = 1 / NetUpdateFrequency
SetNetUpdateFrequency(10.0f);
여기서 중요한 표현은 전송 횟수가 아니라 검사 기회다. 검사 시점이 왔더라도 다음 조건에 따라 실제 패킷이 전송되지 않을 수 있다.
- 복제 프로퍼티가 이전 값과 동일한 경우
- 해당 연결에서 액터가 relevant하지 않은 경우
- 액터가 dormant 상태인 경우
- 연결의 대역폭이 부족해 이번 전송에서 밀린 경우
- 서버 틱이나 네트워크 드라이버의 처리 간격이 더 느린 경우
반대로 값을 크게 설정한다고 네트워크가 더 정확해지는 것도 아니다. 변하지 않는 상태를 자주 검사하면 CPU 비용만 증가할 수 있다.
UE 5.5 프로젝트에서 엔진 생성자 기준으로 확인되는 대표 기본값은 다음과 같다. 기본값은 엔진 버전과 클래스 구현에 따라 바뀔 수 있으므로 다른 버전에서는 클래스 기본 객체나 엔진 소스를 다시 확인해야 한다.
| 클래스 | NetUpdateFrequency | NetPriority |
AActor |
100 | 1 |
APawn |
100 | 3 |
APlayerController |
100 | 3 |
APlayerState |
1 | 1 |
AGameStateBase |
100 | 10 |
UE 5.5에서는 NetUpdateFrequency 멤버를 직접 수정하기보다 다음 접근자를 사용하는 편이 좋다.
SetNetUpdateFrequency(5.0f);
SetMinNetUpdateFrequency(2.0f);
Adaptive Net Update Frequency가 활성화된 환경에서는 액터의 프로퍼티가 오랫동안 변하지 않을 때 검사 빈도가 MinNetUpdateFrequency 방향으로 낮아질 수 있다. 다시 변경이 감지되면 필요한 빈도로 올라간다. 자주 움직이지 않는 액터에 높은 고정 빈도를 유지하는 것보다 효율적이지만, 실제 적용 여부와 동작은 프로젝트의 네트워크 설정을 함께 확인해야 한다.
낮은 빈도와 화면의 부드러움은 별개 문제
네트워크 업데이트 빈도를 낮추면 스냅샷 사이의 간격이 길어진다. 이 값을 클라이언트 화면에 그대로 적용하면 움직임이 계단처럼 끊겨 보인다. 이때는 클라이언트가 받은 상태 사이를 보간하거나, 속도를 이용해 짧은 구간을 예측한 뒤 다음 서버 값으로 보정한다.
void ADXBox::OnRep_ServerRotationYaw()
{
InterpolationStartYaw = GetActorRotation().Yaw;
InterpolationTargetYaw = ServerRotationYaw;
InterpolationElapsedTime = 0.0f;
}
복제 빈도는 네트워크 전송 예산이고, 보간은 화면 표현의 품질이다. 둘은 함께 조정해야 하지만 같은 기능은 아니다. 서버의 권위 있는 충돌이나 판정 상태까지 클라이언트 보간값으로 대체해서도 안 된다.
Relevancy: 이 연결에 필요한 액터인가
Relevancy는 액터가 특정 클라이언트 연결에 필요한지를 판단한다. 하나의 액터가 클라이언트 A에게는 relevant하고, 같은 시점에 멀리 떨어진 클라이언트 B에게는 irrelevant할 수 있다.
이 판단이 연결별로 수행되는 이유는 플레이어마다 위치, 소유 관계, 시야 대상이 다르기 때문이다.
| 설정 또는 관계 | 의미 | 대표 사용 사례 |
bAlwaysRelevant |
모든 연결에서 항상 relevant하게 취급한다. | 전체 경기 상태처럼 모두가 알아야 하는 액터 |
bOnlyRelevantToOwner |
소유 연결에만 relevant하다. | 개인 UI 데이터, 소유자 전용 액터 |
bNetUseOwnerRelevancy |
자신의 판단 대신 Owner의 relevancy와 priority를 따른다. | 무기, 부착물, 소유 액터와 함께 움직이는 구성 요소 |
NetCullDistanceSquared |
거리 기반 relevancy의 제곱 거리 기준이다. | 멀리 떨어지면 보낼 필요가 없는 월드 액터 |
| Owner 또는 Instigator 관계 | 소유자와 행동을 발생시킨 액터를 relevancy 판단에 반영한다. | 플레이어 소유 오브젝트, 투사체 |
NetCullDistanceSquared는 이름 그대로 거리의 제곱을 저장한다. 거리 5,000 언리얼 유닛을 기준으로 삼으려면 제곱값을 설정한다.
SetNetCullDistanceSquared(FMath::Square(5000.0f));
Viewer와 ViewTarget
기본 relevancy 판단은 IsNetRelevantFor()에서 이루어진다.
virtual bool IsNetRelevantFor(
const AActor* RealViewer,
const AActor* ViewTarget,
const FVector& SrcLocation
) const;
| 인자 | 역할 |
RealViewer |
해당 연결을 대표하는 PlayerController다. |
ViewTarget |
현재 그 연결이 바라보는 대상이다. 일반적으로 Possess한 Pawn이지만 관전 상태에서는 달라질 수 있다. |
SrcLocation |
거리 기반 판단에 사용하는 시점 위치다. |
기본 판단에는 다음과 같은 규칙이 관여한다.
- 항상 relevant이거나 Viewer, ViewTarget, Owner, Instigator 관계라면 우선 relevant로 취급한다.
bNetUseOwnerRelevancy가 활성화되어 있으면 Owner의 판단에 위임한다.bOnlyRelevantToOwner인데 현재 연결이 소유자가 아니면 제외한다.- 부착된 액터라면 부모 또는 베이스 액터와의 관계를 반영한다.
- 숨겨져 있고 충돌도 없는 액터는 보낼 필요가 없는 것으로 판단될 수 있다.
- 거리 기반 relevancy가 활성화되어 있으면 cull distance를 검사한다.
PlayerController가 모든 클라이언트에 존재하지 않고 자신의 소유 클라이언트에만 복제되는 것도 이 연결별 relevancy와 소유 관계 때문이다. 반면 PlayerState는 다른 플레이어의 이름과 점수처럼 전체가 알아야 하는 정보를 담는 경우가 많아 기본적으로 항상 relevant하게 설계되어 있다.
NetPriority: 대역폭이 부족할 때 무엇을 먼저 보낼 것인가
Relevancy 검사를 통과했다고 해서 모든 액터가 같은 순서로 전송되는 것은 아니다. 한 연결에 보낼 데이터가 대역폭 예산을 초과하면 서버는 상대적인 priority를 이용해 더 중요한 액터부터 처리한다.
NetPriority는 고정된 전송 횟수를 뜻하지 않는다. 2.0f로 설정했다고 정확히 초당 두 번 더 전송되는 것이 아니라, 다른 조건이 같을 때 더 높은 상대적 기회를 갖는다는 의미다.
NetPriority = 2.0f;
모든 액터의 priority를 같은 비율로 올리는 것도 의미가 없다. 중요한 것은 연결 안에서의 상대적인 비율이다.
실제 우선순위 계산에는 기본 NetPriority 외에도 다음 요소가 반영된다.
- 마지막 복제 이후 경과 시간
- Viewer 및 ViewTarget과의 관계
- Owner relevancy를 사용하는 경우 Owner의 priority
- 거리와 시선 방향
- Instigator 관계
마지막 복제 이후 시간이 반영되므로 낮은 priority의 액터가 영원히 전송되지 않는 starvation을 완화할 수 있다. 또한 대역폭이 충분하다면 priority 차이와 무관하게 relevant한 후보들이 모두 전송될 수 있다. 즉 priority 튜닝은 주로 네트워크가 포화되는 상황에서 의미가 있다.
| 설정 | 결정하는 질문 | 적용 범위 |
NetUpdateFrequency |
이 액터를 언제 다시 검사할 것인가? | 액터의 시간축 |
| Relevancy | 이 연결에 이 액터가 필요한가? | 액터와 연결의 관계 |
NetPriority |
대역폭이 부족할 때 무엇을 먼저 보낼 것인가? | 연결별 전송 후보의 순서 |
Priority만 높이면 relevancy나 dormancy를 무시하고 전송되는 것은 아니다. 앞 단계에서 후보에서 제외된 액터는 우선순위 경쟁에도 참여하지 않는다.
Net Dormancy: 변하지 않는 액터를 검사 대상에서 제외한다
월드에 배치된 문, 보물 상자, 스위치처럼 대부분의 시간 동안 상태가 변하지 않는 액터도 매 복제 주기마다 변경 여부를 검사하면 서버 CPU를 사용한다. Net Dormancy는 이런 액터를 복제 검사 대상에서 제외하는 최적화다.
| Dormancy 상태 | 의미 |
DORM_Never |
휴면 상태를 사용하지 않는다. |
DORM_Awake |
현재 깨어 있으며 일반적인 복제 검사에 참여한다. |
DORM_DormantAll |
모든 연결에 대해 휴면 상태다. |
DORM_DormantPartial |
연결별 부분 휴면을 위한 상태지만 현재는 사용이 권장되지 않는다. |
DORM_Initial |
맵에 처음부터 배치된 액터가 초기 상태에서 휴면하도록 한다. |
DORM_Initial은 레벨 배치 액터를 위한 상태다. 서버가 런타임에 동적으로 스폰하는 액터의 초기 dormancy로 사용해서는 안 된다.
한 번만 상태를 갱신할 때
휴면 액터의 복제 프로퍼티를 변경해야 한다면 값을 바꾸기 전에 먼저 dormancy를 flush해야 한다.
void ADXBox::SetLightColorOnce(const FLinearColor& NewColor)
{
check(HasAuthority());
FlushNetDormancy();
ServerLightColor = NewColor;
}
FlushNetDormancy()는 적어도 한 번의 복제 업데이트가 가능하도록 액터를 처리하지만, 일반적인 경우 dormancy 상태 자체를 계속 DORM_Awake로 바꾸지는 않는다. DORM_Initial 상태에서 flush하면 이후에는 DORM_DormantAll로 전환된다.
프로퍼티를 먼저 변경하고 나중에 flush하는 순서에 의존하면 복제 변경 목록이 올바르게 준비되지 않을 수 있으므로, 항상 깨운 뒤 값을 변경하는 패턴을 유지하는 편이 안전하다.
일정 시간 동안 계속 변경할 때
문이 열리는 애니메이션처럼 여러 업데이트가 이어진다면 액터를 명시적으로 깨우고, 변경이 끝난 뒤 다시 휴면 상태로 전환한다.
void ADXDoor::BeginOpening()
{
check(HasAuthority());
SetNetDormancy(DORM_Awake);
bIsOpening = true;
}
void ADXDoor::FinishOpening()
{
check(HasAuthority());
bIsOpening = false;
bIsOpen = true;
ForceNetUpdate();
SetNetDormancy(DORM_DormantAll);
}
ForceNetUpdate()는 다음 업데이트를 앞당길 때 사용할 수 있으며, dormant 액터에 호출하면 dormancy flush도 수행한다. 다만 아주 짧은 간격으로 수면과 깨우기를 반복하면 채널과 변경 상태를 다시 준비하는 비용이 커질 수 있다. 자주 변하는 액터라면 계속 awake로 두고 update frequency를 조정하는 편이 나을 수 있다.
Relevancy와 Dormancy는 결과가 다르다
두 기능 모두 전송량을 줄이지만 액터가 클라이언트에서 유지되는 방식은 다르다.
| 구분 | Relevancy에서 제외 | Dormancy 진입 |
| 판단 단위 | 연결별 | 기본적으로 액터의 휴면 상태 |
| 클라이언트 액터 | 동적 복제 액터는 연결에서 제거될 수 있다. | 기존 액터를 유지한 채 업데이트 검사를 멈춘다. |
| 다시 필요할 때 | relevant해지면 다시 복제된다. | 서버가 wake 또는 flush한 뒤 변경값을 보낸다. |
| 주요 목적 | 해당 플레이어에게 불필요한 액터 제거 | 오랫동안 변하지 않는 액터의 서버 검사 비용 절감 |
RPC 호출만으로 dormant 프로퍼티의 최신 상태까지 자동 전송된다고 가정해서는 안 된다. RPC와 함께 복제 상태가 바뀐다면 먼저 액터를 wake 또는 flush하고 프로퍼티를 변경해야 한다.
Conditional Property Replication: 액터 안에서도 수신자를 나눈다
Relevancy는 액터 전체가 해당 연결에 필요한지를 결정한다. 액터는 필요하지만 특정 프로퍼티만 일부 연결에 보내고 싶다면 조건부 프로퍼티 복제를 사용한다.
void ADXBox::GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps
) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(
ThisClass,
ServerLightColor,
COND_InitialOnly
);
}
| 조건 | 전송 대상 | 대표 사례 |
COND_None |
조건 없이 일반 복제 | 모든 relevant 연결이 알아야 하는 상태 |
COND_InitialOnly |
액터가 처음 복제될 때만 전송 | 생성 후 바뀌지 않는 설정값 |
COND_OwnerOnly |
소유 연결에만 전송 | 개인 정보나 소유자 전용 상태 |
COND_SkipOwner |
소유자를 제외한 연결에 전송 | 소유자가 이미 로컬 예측으로 처리한 상태 |
COND_SimulatedOnly |
Simulated Proxy에만 전송 | 원격 표현에만 필요한 상태 |
COND_AutonomousOnly |
Autonomous Proxy에만 전송 | 소유 Pawn의 예측과 보정에 필요한 상태 |
COND_Custom |
런타임에 활성 여부를 제어 | 고정 조건으로 표현하기 어려운 특수 상태 |
Lifetime Replication에 등록한 프로퍼티는 액터 인스턴스마다 임의로 등록 해제하는 구조가 아니다. 수신 조건이 필요하다면 DOREPLIFETIME_CONDITION 계열을 사용하고, 복잡한 custom 조건은 평가 비용과 상태 관리 비용까지 고려해야 한다.
하나의 복제 파이프라인으로 이해하기
각 설정이 개입하는 시점을 합치면 다음과 같다.
1. bReplicates가 꺼져 있으면 액터 복제 대상이 아니다.
2. Dormancy 상태면 일반적인 복제 검사에서 제외한다.
3. NetUpdateFrequency에 따라 이번에 검사할 시점인지 판단한다.
4. 각 연결에서 소유 관계와 Relevancy를 검사한다.
5. 전송 후보가 많으면 NetPriority로 처리 순서를 정한다.
6. 액터의 변경된 프로퍼티와 복제 조건을 평가한다.
7. 선택된 데이터를 직렬화해 클라이언트에 보낸다.
8. 클라이언트는 값을 반영하고 필요한 RepNotify를 호출한다.
이 순서를 알면 “priority를 높였는데 왜 전송되지 않는가?” 같은 문제도 단계별로 추적할 수 있다. 액터가 dormant하거나 해당 연결에서 irrelevant하다면 priority를 평가하는 단계까지 도달하지 않는다.
액터 성격에 따른 설정 전략
모든 복제 액터에 같은 설정을 적용하는 대신 변화 빈도와 수신 범위를 기준으로 분류하는 편이 좋다.
| 액터 또는 데이터 | Update Frequency | Relevancy | Dormancy 및 조건 |
| 플레이어 Pawn | 움직임 품질에 맞게 비교적 높게 유지 | 거리와 게임 규칙을 반영 | 일반 플레이 중 awake |
| 문, 상자, 스위치 | 낮게 설정 가능 | 주변 플레이어에게만 필요할 수 있음 | 평소 dormant, 상호작용 전에 wake 또는 flush |
| 소유자 전용 안내 액터 | 변경 빈도에 맞게 설정 | bOnlyRelevantToOwner |
프로퍼티에도 COND_OwnerOnly 검토 |
| 생성 후 고정되는 설정값 | 액터 특성에 맞게 설정 | 필요한 연결에만 relevant | COND_InitialOnly |
| 로컬 장식 효과 | 해당 없음 | 해당 없음 | 가능하면 액터 복제 자체를 사용하지 않음 |
일반적인 최적화 순서는 다음과 같이 잡을 수 있다.
- 네트워크에 필요 없는 액터는
bReplicates를 끈다. - 자주 변하지 않는 액터는
NetUpdateFrequency를 낮춘다. - 오랫동안 변하지 않는 액터는 dormancy를 사용한다.
- 모든 플레이어에게 필요하지 않다면 relevancy 범위를 줄인다.
- 액터는 필요하지만 일부 데이터만 선택적으로 보내려면 replication condition을 사용한다.
- 위치, 회전, 실수 값은 허용 가능한 범위에서 양자화와 더 작은 자료형을 검토한다.
Priority 조정은 이 기본적인 데이터 절감 이후에 진행하는 편이 좋다. 불필요한 데이터를 그대로 둔 채 priority만 바꾸면 전체 비용은 줄지 않고, 포화 시 어떤 액터가 먼저 밀리는지만 바뀐다.
NumberBaseball 프로젝트에 적용해 보기
현재 프로젝트의 ANBPlayerState는 다음 세 값을 일반 Property Replication으로 등록한다.
DOREPLIFETIME(ThisClass, PlayerNameString);
DOREPLIFETIME(ThisClass, CurrentGuessCount);
DOREPLIFETIME(ThisClass, MaxGuessCount);
PlayerNameString과 CurrentGuessCount는 다른 플레이어의 정보 표시에도 사용될 수 있으므로 PlayerState의 전체 공개 성격과 잘 맞는다. 반면 MaxGuessCount가 경기 도중 절대 바뀌지 않는 규칙값이라면 설계를 두 방향으로 검토할 수 있다.
- 플레이어마다 최대 횟수가 다르다면 PlayerState에 두되
COND_InitialOnly를 사용한다. - 모든 플레이어가 같은 값을 공유한다면 GameState 같은 경기 공용 상태에 한 번만 보관한다.
DOREPLIFETIME_CONDITION(
ThisClass,
MaxGuessCount,
COND_InitialOnly
);
숫자 야구처럼 액터 수와 상태 변경 빈도가 낮은 게임에서는 기본 설정만으로도 문제가 드러나지 않을 수 있다. 하지만 연결 수와 월드 액터가 증가하면 “누가 가져야 하는 데이터인가”와 “얼마나 자주 바뀌는가”를 먼저 분리한 설계가 확장성을 결정한다.
복제가 기대대로 동작하지 않을 때 확인할 항목
| 증상 | 우선 확인할 항목 |
| 클라이언트에 액터가 보이지 않는다. | bReplicates, 서버에서의 스폰 여부, bNetLoadOnClient, relevancy와 cull distance를 확인한다. |
| 프로퍼티를 바꿨는데 늦게 도착한다. | NetUpdateFrequency, dormancy, 대역폭 포화, priority를 확인한다. |
| dormant 액터의 값이 갱신되지 않는다. | 값을 변경하기 전에 FlushNetDormancy() 또는 SetNetDormancy(DORM_Awake)를 호출했는지 확인한다. |
| 특정 클라이언트만 값을 받지 못한다. | Owner 체인, bOnlyRelevantToOwner, replication condition을 확인한다. |
| 업데이트 빈도는 높은데 화면이 끊긴다. | 패킷 손실뿐 아니라 클라이언트 보간, 예측, 서버 보정 로직을 함께 확인한다. |
| priority를 높여도 전송되지 않는다. | 그 전에 dormancy, update frequency, relevancy 단계에서 제외되지 않았는지 확인한다. |
복제 문제는 한 옵션만 확인해서는 찾기 어렵다. 액터가 생성되었는지, 이번 주기의 후보인지, 해당 연결에서 relevant한지, 대역폭 경쟁에서 선택되었는지, 프로퍼티 조건을 통과했는지를 파이프라인 순서대로 추적해야 한다.
'UE > 멀티플레이' 카테고리의 다른 글
| UE C++ 멀티플레이 동기화 (0) | 2026.08.07 |
|---|---|
| UE C++ RPC오너십, 프로퍼티 리플리케이션 (1) | 2026.08.06 |
| UE C++ 멀티플레이 게임 프레임워크 (1) | 2026.08.04 |
| 언리얼C++ 멀티플레이 숫자야구 (판정 및 게임사이클 완성) (0) | 2026.08.03 |
| [Unreal 9기] 2026-07-31 UE 넷모드, 넷드라이브, 리플리케이션 (1) | 2026.07.31 |