UE5 Network Prediction Plugin — 예측 이동 구현
Network Prediction Plugin(NP)은 UE5에서 실험적으로 도입된 데이터 중심 네트워크 예측 프레임워크입니다. 기존 CharacterMovementComponent의 한계(커스터마이징 어려움, 모노리식 구조)를 극복하고, 임의의 시뮬레이션 상태에 클라이언트 예측과 서버 조정을 적용할 수 있습니다.
1. 플러그인 활성화
섹션 제목: “1. 플러그인 활성화”Edit > Plugins > Network Prediction → 활성화 (실험적)
Build.cs:PublicDependencyModuleNames.AddRange(new[]{ "NetworkPrediction", "NetworkPredictionExtras",});2. 시뮬레이션 상태 정의
섹션 제목: “2. 시뮬레이션 상태 정의”#include "NetworkPredictionTypes.h"
// 입력 상태: 클라이언트가 매 틱 전송struct FMyInputCmd{ FVector2D MoveInput; // 이동 방향 bool bJump = false; bool bSprint = false;
void NetSerialize(FArchive& Ar) { Ar << MoveInput; Ar.SerializeBits(&bJump, 1); Ar.SerializeBits(&bSprint, 1); }};
// 동기 상태: 서버가 권위를 가지는 시뮬레이션 상태struct FMySyncState{ FVector Location; FVector Velocity; FRotator Rotation; bool bIsGrounded = true;
bool ShouldReconcile(const FMySyncState& Auth) const { // 서버 상태와 예측 상태 차이가 임계값 초과 시 조정 return FVector::DistSquared(Location, Auth.Location) > 25.f; }
void NetSerialize(FArchive& Ar) { Ar << Location << Velocity << Rotation; Ar.SerializeBits(&bIsGrounded, 1); }};
// 보조 상태: 클라이언트에서 보간되는 비예측 데이터struct FMyAuxState{ float StaminaLevel = 100.f; float Health = 100.f;
void NetSerialize(FArchive& Ar) { Ar << StaminaLevel << Health; }};3. 시뮬레이션 구현
섹션 제목: “3. 시뮬레이션 구현”#include "NetworkPredictionSimulation.h"
struct FMyMovementSimulation{ // 핵심: 시뮬레이션 틱 (클라이언트/서버 공통 실행) static void Tick( float DeltaTime, const FMyInputCmd& Input, const FMySyncState& InputState, FMySyncState& OutputState, const FMyAuxState& AuxState) { // 이동 계산 (결정론적이어야 함) FVector Acceleration = FVector(Input.MoveInput.X, Input.MoveInput.Y, 0.f) * 600.f;
// 중력 FVector Gravity = InputState.bIsGrounded ? FVector::ZeroVector : FVector(0.f, 0.f, -980.f);
// 속도 통합 OutputState.Velocity = InputState.Velocity + (Acceleration + Gravity) * DeltaTime;
// 스프린트 if (Input.bSprint && AuxState.StaminaLevel > 0.f) OutputState.Velocity *= 1.8f;
// 최대 속도 제한 OutputState.Velocity = OutputState.Velocity.GetClampedToMaxSize(700.f);
// 위치 통합 OutputState.Location = InputState.Location + OutputState.Velocity * DeltaTime;
// 점프 if (Input.bJump && InputState.bIsGrounded) OutputState.Velocity.Z = 500.f;
OutputState.Rotation = InputState.Rotation; OutputState.bIsGrounded = InputState.bIsGrounded; // 콜리전 처리 필요 }};
// 타입 등록 매크로DEFINE_NETWORK_PREDICTION_TYPE_OUTER( FMyInputCmd, FMySyncState, FMyAuxState, FMyMovementSimulation);4. NetworkPredictionComponent 설정
섹션 제목: “4. NetworkPredictionComponent 설정”#include "NetworkPredictionComponent.h"
UCLASS()class AMyCharacter : public ACharacter{ GENERATED_BODY()
UPROPERTY(VisibleAnywhere) UNetworkPredictionComponent* NPComp;
public: AMyCharacter() { // 기존 CharacterMovement 비활성화 (NP 사용 시) GetCharacterMovement()->SetActive(false);
NPComp = CreateDefaultSubobject<UNetworkPredictionComponent>( TEXT("NetworkPrediction")); }
virtual void BeginPlay() override { Super::BeginPlay();
// 시뮬레이션 초기화 FMySyncState InitialState; InitialState.Location = GetActorLocation(); InitialState.Velocity = FVector::ZeroVector;
NPComp->InitializeSimulation<FMyMovementSimulation>( this, InitialState); }
// 입력 생성 (매 틱) virtual void ProduceInput( int32 SimFrame, FMyInputCmd& Cmd) { // 플레이어 입력 읽기 Cmd.MoveInput.X = GetInputAxisValue("MoveForward"); Cmd.MoveInput.Y = GetInputAxisValue("MoveRight"); Cmd.bJump = GetInputAxisValue("Jump") > 0.5f; Cmd.bSprint = GetInputAxisValue("Sprint") > 0.5f; }};5. Cue 시스템 — 비예측 이벤트
섹션 제목: “5. Cue 시스템 — 비예측 이벤트”// Cue: 점프/착지 이펙트처럼 예측은 안 하지만 표시해야 하는 이벤트struct FJumpCue{ FVector Location; float Intensity = 1.f;
static void OnActive( const USceneComponent* Owner, const FJumpCue& Cue) { // 착지 이펙트, 사운드 재생 (예측 불필요) UGameplayStatics::SpawnEmitterAtLocation( Owner->GetWorld(), LandingParticle, Cue.Location); }};6. 기존 CMC와 비교
섹션 제목: “6. 기존 CMC와 비교”| 항목 | CharacterMovementComponent | Network Prediction Plugin |
|---|---|---|
| 상태 | 모노리식 | 타입 분리 (Input/Sync/Aux) |
| 커스터마이징 | 오버라이드 복잡 | Tick 함수만 구현 |
| 결정론 | 부분적 | 강제 (필수) |
| 성숙도 | 안정 | 실험적 |
| 복잡도 | 낮음 | 높음 |
Network Prediction Plugin은 아직 실험적이므로 프로덕션 적용 전 안정성을 충분히 검증하세요. 시뮬레이션 Tick 함수는 **결정론적(deterministic)**이어야 합니다 — 같은 입력과 상태에 대해 항상 같은 결과를 내야 합니다. ShouldReconcile에서 위치 오차가 임계값을 넘을 때만 서버 조정이 일어나도록 튜닝해 네트워크 트래픽을 최소화하세요.