Actor Replication 시작하기
8강. Actor Replication 시작하기
1. 이번 강의에서 해결할 문제
서버가 Spawn한 점령 지점이 클라이언트에 없거나, 클라이언트가 로컬 Spawn한 Actor가 서버에는 존재하지 않습니다. bReplicates와 서버 Spawn 경계를 이해하지 않으면 같은 모양의 서로 다른 Actor를 만들고 충돌·점수를 서로 다른 객체에서 계산하게 됩니다.
2. 학습 목표
3. 핵심 개념
Actor Replication은 서버 Actor의 생성·삭제와 등록된 상태를 관련 클라이언트에 전달합니다. 기본 전제는 서버가 권위 있는 Actor를 Spawn하는 것입니다. 클라이언트가 만든 non-authority Actor는 로컬 장식일 뿐 서버가 모르며 다른 클라이언트로 전파되지 않습니다.
bReplicates는 Actor가 네트워크 복제 대상이 될 수 있게 합니다. SetReplicateMovement(true)는 Actor의 위치·회전·속도 관련 이동 상태를 기본 경로로 복제합니다. NetUpdateFrequency를 무조건 높이면 대역폭과 서버 작업이 늘며 즉시성 문제를 구조 대신 빈도로 가릴 수 있습니다.
복제 흐름을 읽기 위한 용어
| 용어 | 의미 | 주의할 점 |
|---|---|---|
| 권위 Actor | 서버 World에서 게임 상태의 원본이 되는 Actor | Spawn·Destroy와 지속 상태 판정을 서버가 수행합니다. |
| Client Proxy | 서버 Actor를 나타내기 위해 Client World에 생성된 별도 Actor 객체 | 서버 객체와 메모리 주소가 같지 않습니다. |
| NetDriver | 연결별 Actor 복제와 패킷 송수신을 조정하는 시스템 | bReplicates만으로 모든 연결에 항상 보내는 것은 아닙니다. |
| Relevancy | 특정 연결에 이 Actor를 보내야 하는지 결정하는 조건 | 거리·소유·항상 관련 설정 등에 따라 달라집니다. |
| Network GUID | 서버 Actor와 Client Proxy를 네트워크에서 대응시키는 식별 | 게임 저장용 영구 ID로 사용하지 않습니다. |
| Movement Replication | 서버 Transform·속도 관련 상태를 기본 경로로 전달 | 커스텀 물리나 예측 요구를 모두 해결하지는 않습니다. |
데이터 흐름은 “서버 객체가 Client로 이동”하는 것이 아닙니다.
서버 GameMode가 Beacon Spawn
→ 서버 World에 권위 Actor 생성
→ NetDriver가 연결별 Relevancy 평가
→ 관련 Client에 네트워크 생성 정보 전달
→ 각 Client World에 별도 Proxy Actor 생성
→ 서버 상태·이동 갱신 적용
→ 서버 Destroy 통지
→ 각 Client Proxy 제거
Client 로컬 Spawn은 이 흐름의 서버 원본과 Network GUID 대응이 없으므로 다른 머신에 전파되지 않습니다. 로컬 VFX라면 의도한 동작일 수 있지만 점령 지점·아이템·데미지 판정 Actor에는 사용할 수 없습니다.
4. 단계별 실습
파일 경로: Source/CaptureArena/World/CaptureBeacon.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CaptureBeacon.generated.h"
UCLASS()
class CAPTUREARENA_API ACaptureBeacon : public AActor
{
GENERATED_BODY()
public:
ACaptureBeacon();
protected:
UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TObjectPtr<UStaticMeshComponent> Mesh;
};
#include "World/CaptureBeacon.h"
#include "Components/StaticMeshComponent.h"
ACaptureBeacon::ACaptureBeacon()
{
PrimaryActorTick.bCanEverTick = false;
bReplicates = true;
SetReplicateMovement(true);
NetUpdateFrequency = 10.0f;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
SetRootComponent(Mesh);
}
생성자 설정 한 줄씩 이해하기
- Beacon은 매 프레임 자체 로직이 없으므로 Tick을 끕니다.
bReplicates = true는 이 Actor를 NetDriver가 복제 후보로 다루게 합니다. 이미 Spawn된 뒤 늦게 켜기보다 클래스 기본값으로 명시합니다.SetReplicateMovement(true)는 서버 RootComponent 이동 상태를 Client Proxy에 전달합니다. 이것은bReplicates와 별도 선택입니다.NetUpdateFrequency = 10은 목표 갱신 빈도 힌트이지 정확히 0.1초마다 패킷을 보장하는 타이머가 아닙니다.- Mesh는 Default Subobject이므로 서버와 Client에서 같은 클래스 구조로 생성됩니다. 실제 Mesh Asset은 Blueprint 기본값으로 지정할 수 있습니다.
서버 Spawn · CaptureArenaGameMode.cpp
void ACaptureArenaGameMode::BeginPlay()
{
Super::BeginPlay();
if (!ensure(HasAuthority()) || !BeaconClass)
{
return;
}
FActorSpawnParameters Params;
Params.SpawnCollisionHandlingOverride =
ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
GetWorld()->SpawnActor<ACaptureBeacon>(
BeaconClass, BeaconTransform, Params);
}
GameMode는 서버에만 존재하므로 이 BeginPlay는 Client에서 실행되지 않습니다. HasAuthority() 검사는 서버 전용 클래스 안에서도 의도를 문서화하고 잘못된 호출 경로를 빠르게 드러냅니다. BeaconClass는 서버가 허용한 클래스이고 Spawn 위치도 서버 규칙으로 정해야 합니다.
PIE에서 복사본을 구분하는 최소 로그
이전 강의의 CaptureNetDebug::Context를 사용해 Beacon BeginPlay에 임시 로그를 추가합니다.
UE_LOG(LogTemp, Display, TEXT("%s Beacon BeginPlay Authority=%d Role=%s"),
*CaptureNetDebug::Context(this),
HasAuthority(),
*UEnum::GetValueAsString(GetLocalRole()));
3인 Listen Server PIE라면 Beacon 하나에 대해 서버에서 Authority 로그 한 줄, 두 Client에서 Simulated Proxy 로그가 각각 한 줄씩 보여야 합니다. 로그의 객체 주소나 PIE 접미사는 서로 달라도 같은 서버 Spawn에 대응하는 Proxy입니다.
리슨 PIE에서 서버가 Spawn한 Beacon의 이름과 Role을 두 창에서 비교합니다. 다음으로 클라이언트 콘솔 경로에서 로컬 Spawn을 시도하고 서버·다른 클라이언트에 나타나지 않는 증상을 기록한 뒤 코드를 제거합니다.
5. 코드가 동작하는 이유
GameMode는 서버에만 존재하므로 Spawn 호출이 권위 있는 World에서 실행됩니다. bReplicates가 켜진 Actor는 NetDriver가 연결별로 고려하고 관련 클라이언트에 네트워크 생성 정보를 보냅니다. 클라이언트 복사본은 서버 Actor와 네트워크 GUID로 대응하지만 별도 메모리 객체입니다. 서버 Destroy도 같은 권위 경로로 전파됩니다.
6. 자주 하는 실수와 해결법
7. 직접 실습
실습 목표
서버 Spawn·이동·Destroy가 두 Client Proxy에 전달되는 흐름과, Client 로컬 Spawn이 전파되지 않는 차이를 PIE 로그와 화면으로 검증합니다.
시작 전 상태
Player 수 3의 Listen Server PIE를 사용하고 BP_CaptureBeacon에 눈에 보이는 Mesh를 지정합니다. BeginPlay 로그에 NetMode·PIE ID·Role이 있어야 합니다.
1단계: 따라 하기
서버 GameMode에서 Beacon 하나를 Spawn합니다. Host와 Client 두 창 모두 같은 위치에 Beacon이 보이고, 서버는 Authority, Client 둘은 Simulated Proxy인지 확인합니다.
2단계: 값 바꿔 보기
서버에서 Beacon 두 개를 서로 다른 위치에 Spawn하고 5초 뒤 하나를 Destroy합니다. 세 World에서 생성·삭제 순서는 같아야 하지만 프레임과 수신 시각은 조금 다를 수 있습니다.
3단계: 직접 적용
SetReplicateMovement(false)로 바꾸고 서버에서 Beacon을 이동합니다. 서버 화면만 새 위치를 보이고 Client Proxy는 초기 위치에 남는지 확인한 뒤 다시 true로 복원합니다. 다음으로 Client 전용 테스트 경로에서 로컬 Spawn한 장식 Actor가 그 Client에만 존재하는지 기록하고 테스트 코드를 제거합니다.
4단계: 스스로 확인
막혔을 때
| 증상 | 먼저 확인할 곳 | 해결 |
|---|---|---|
| 서버에만 Beacon이 보임 | bReplicates, Relevancy, 서버 Spawn 여부 | 생성자 설정과 서버 BeginPlay 로그, Client 연결을 확인합니다. |
| Client마다 Beacon 수가 다름 | Client 로컬 Spawn 또는 조건별 서버 중복 Spawn | Spawn 호출의 NetMode·Authority 로그를 남기고 GameMode 경로 하나로 모읍니다. |
| Actor는 보이지만 이동이 안 맞음 | Replicate Movement가 꺼졌거나 Root 이동이 서버가 아님 | 서버 RootComponent 변경과 SetReplicateMovement를 확인합니다. |
| Destroy가 Client에 남음 | Client에서만 Destroy하거나 Actor가 서버 원본과 무관 | 권위 Actor에서 Destroy()가 호출됐는지 로그로 확인합니다. |
8. 이해 점검 질문 3개
9. 핵심 요약
다음 강의 연결
다음 강의: Replicated 변수와 RepNotify에서는 생성된 Client Proxy에 점령 상태·진행률 같은 지속 값을 등록하고 서버 변경을 UI에 전달합니다.
Actor Replication 시작하기 미니 퀴즈
선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.
학습을 마쳤나요?
직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.