본문 바로가기
Unreal 게임 개발/Unreal 강의 개인 정리

움직임 리플리케이션 플로우 & Simulated Proxy 리플리케이션 - Unreal Network MultiPlayer Framework

by daisy0461 2025. 3. 11.

Simulated Proxy & 움직임 리플리케이션을 사용하기 위해선

Actor의 움직임 리플리케이션 옵션을 활성화해야 올바르게 동작한다.

Actor 움직임 리플리케이션 플로우

  1. 서버에서 움직임 리플리케이션 옵션을 활성화
  2. 매 Tick마다 현재 Actor의 움직이나 물리 상태를 ReplicatedMovement라는 멤버 변수에 저장
  3. ReplicatedMovement라는 멤버 변수를 클라이언트의 SimulatedProxy에 전송
  4. OnRep_ReplicatedMovement함수 호출
    클라이언트의 움직임이나 물리상태를 기록한다.
  5. 클라이언트는 Tick마다 물리 & 움직임 처리를 진행한다.
    물리가 활성화되어있다면 물리 리플리케이션 Tick에서 클라이언트와 서버에 물리상태를 동기화
    캐릭터는 CharacterMovement의 매 Tick마다 캐릭터와 관련된 움직임을 처리한다.

 

Actor 물리 움직임 & 일반 움직임 리플리케이션 중요 변수 및 함수

ReplicatedMovement

서버에서 Simulated Proxy로 보내는 움직임 정보를 기록한 멤버 변수

  1. 프로퍼티 리플리케이션을 사용하기에 OnRep_ReplicatedMovement로 이벤트 함수를 클라이언트에서 호출한다.
  2. 일반 움직임과 물리 움직임의 리플리케이션을 모두 처리하는 용도로 활용된다.

FRepMovement 구조체의 멤버 변수 (ReplicatedMovemnet 변수의 구조체이다)

  1. 위치와 회전 :컴포넌트의 현재 위치와 회전
  2. 데이터 정밀도 설정 : 위치, 회전, 속도의 데이터 정밀도. 끊김 현상이 보이지 않을 만큼 최소로 설정
  3. 물리 시뮬레이션 여부 플래그 : 물리 시뮬레이션으로 복제할지 지정. 해당 옵션에 따라 내부에선 완전히 다른 동작을 수행한다.
  4. 이동속도: 컴포넌트의 이동속도
  5. 각 속도 : 컴포넌트의 각 속도. 물리 시뮬레이션 진행시에만 사용
  6. 서버 프레임 : 서버에서의 물리 프레임
더보기

다음과 같이 FRepMovement 구조체로 ReplicatedMovement가 선언되어있다.

ReplicatedUsing을 사용하며 OnRep_ReplicatedMovement로 지정되어있다.

FRepMovement 구조체의 정의이다.

 

FRigidBodyState

Actor의 물리 상태를 기록하는 구조체이다.

멤버변수

  1. 위치 : 소수점 두자리 정밀도로 기록
  2. 회 : 사원수(Quaternion) 정보로 기록
  3. 속도 : 소수점 두 자리 정밀도로 기록
  4. 각 속도 : 소수점 두 자리 정밀도로 기록
  5. 플래그 : 휴면 상태와 같은 특정 물리 상태를 기록하는데 사용
더보기

다음과 같이 정의되어 있으며 FVector_NetQuantize100이 소수점 두 자리까지 기록하는 정밀도가 낮은 3차원 Vector이다.

 FRigidBodyState와 위의 FRepMovement 구조체는 서로 호환이 되도록 설계가 되어있다.

FillFrom은 FRigidBodyState로부터 ReplicatedMovement 변수 값을 설정하는 함수이며

CopyTo는 반대로 ReplicatedMovement 변수값으로 부터 FRigidBodyStae로 옮겨준다.

 

GatherCurrentMovement()

서버에서 Actor의 움직임 정보를 수집하는 함수이다.

특징

  1. Actor의 ReplicatedMovement 변수 값을 설정하는 역할을 한다.
  2. Actor의 PreReplication 함수에서 호출된다.
    PreReplication 함수는 네트워크로 전송할 Actor들의 모음 Consider List에 속한 Actor들이 
    각 클라이언트에게 패킷을 보내기 전 최종적으로 자신의 상태를 확정하는 함수이다.
    즉, 네트워크 패킷을 보내기 전 GatherCurrentMovement함수로 Actor의 최종 움직임을 정리한다.
  3. Actor의 물리 움직임과 일반 움직임을 구분해서 처리한다.
    일반적인 움직임은 Actor의 위치, 회전, 속도값을 ReplicatedMovement구조체에 설정한다.
    물리 움직임은 현재 월드에 설정된 물리 씬에서 해당 컴포넌트의 물리 상태를 저장한다 (이때 FillFrom 사용)
  4. 마지막으 ReplicatedMovement가 설정되어 클라이언트에 보내진다.
더보기

Actor가 네트워크를 전송하기 전에 상태를 마무리하는 PreReplication에서 GatherCurrentMovement를 호출한다.

 

GatherCurrentMovement함수는 Replicate 옵션이 활성화되어있지 않으면 동작하지 않는다.

 물리 움직임이라면 World에서 물리 씬을 UWorld* World = GetWorld()로 들고오고

FillFrom을 통해 현재 정보를 옮겨주게 된다.

일반적인 움직임이라면 Location, Rotation, LinearVelocity, AngularVelocity의 값들을 지정해서 보내준다.

 

OnRep_ReplicatedMovement()

서버 Actor의 리플리케이티드 무브먼트 변수가 변경되면 클라이언트에서 자동으로 해당 함수가 호출된다.

OnRep_ReplicatedMovement()도 일반적인 움직임과 물리적인 움직임을 나눠서 실행한다.

일반적인 움직임

  1. Simulated Proxy에 대해서만 처리하며 단순하게 컴포넌트의 위치와 회전 정보를 갱신한다.
  2. 속도 처리는 진행하지 않는다.하지만 가상함수로 처리해두어서 사용자가 필요하면 설정이 가능하다.

물리 움직임

  1. ReplicatedMovement의 정보를 현재 컴포넌트의 물리 상태로 옮긴다. (CopyTo 사용)
  2. 물리 리플리케이션 씬에서 컴포넌트와 일치하는 타겟을 찾는다.
  3. 클라이언트가 변경해야할 물리 상태의 목표값을 저장하는 FReplicatedPhysicsTarget구조체의 데이터를 갱신한다.
    FReplicatedPhysicsTarget구조체의 값으로 클라이언트가 계속 물리에 대한 상태를 서버와 맞추도록 한다.
더보기

물리 움직임 플로우

OnRep_ReplicatedMovement()에서 LocalRepMovement.bRepPhysics가 true이다.
즉, 물리적인 움직임이라면 물리적인 처리를 진행한다.

여기서 PostNetReceivePhysicState()라는 함수를 호출해서 물리적인 처리를 진행한다.

 PostNetReceivePhysicState()는 GetReplicateMovement()로 움직임 정보를 받은 후에

위에서 설명한 FRigidBodyState구조체와 함께 CopyTo()를 통해 물리 상태를 카피해준다.

이 물리 상태를 사용해서 SetRigidBodyReplicatedTarget()이라는 함수를 호출한다.

 SetRigidBodyReplicatedTarget()함수는 World에서 GetPhysicsScene으로 물리 씬 정보 들고온다.

SetReplicatedTarget()함수를 활용해 지정한 물리 컴포넌트의 물리 상태를 덮어쓰게 된다.

SetReplicatedTarget()에선 Target값을 FReplicatedPhysicsTarget이라는 구조체로 들고온 뒤

전달 받은 FRigidBodyState값으로 Target을 덮어쓴다.

이제 Target에는 클라이언트가 앞으로 동기화해야할 서버의 물리 상태값을 저장한다.

FReplicatedPhysicsTarget은 TargetState라는 물리 상태에 대한 값, BoneName, ServerFrame, 이전 값에 대한 정보 등

동기화를 위해 필요한 정보가 들어있다.

 

 

일반적인 움직임 플로우

일반적인 움직임이라면 일반적인 움직임에 대한 처리를 한다.

PostNetReceiveVelocity()에서 속도에 대한 처리를 진행하는데 필요하다면 해당 함수를 override해서 구현하면 된다.

PostNetReceiveLocationAndRotation()함수는 

GetReplicatedMovement로 현재 ReplicatedMovement값을 받아와서 
SetActorLocationAndRotation으로 세팅해주는 것으로 간단하게 끝난다.

 

ApplyRigidBodyState

물리 리플리케이션 Tick에서 호출되는 함수이다.

클라이언트의 물리 상태가 서버의 물리 상태와 오차 내에 있을 때 까지 계속 호출된다.

진행 로직

  1. 서버에서 받은 최종 속도와 핑을 기반으로 클라이언트 물리 상태를 외삽(Extrapolation)으로 예측
  2. 예측한 위치와 방향이 서버와 비교해 올바른지 체크 후 문제가 있다고 판단되면 에러 시간을 누적한다.
  3. 누적된 에러 시간 설정값을 넘으면 강제로 조정(하드 스냅)한다.
  4. 차이가 크지 않다면 내삽(Interpolation)을 수행해서 현재 위치, 회전, 속도, 각 속도를 조정한다. 
더보기

PhysicsReplication의 OnTick에서 다양한 조건을 통과하면 ApplyRigidBody를 호출한다.

이제 ApplyRigidBody 안을 살펴보자

Target값을 들고와서 NewState변수에 지정한다. 

이후 기본적인 설정을 한 다음에

CurrentState를 들고온다.

그래서 NewState와 CurrentState에서 차이점을 동기화시켜주는 작업을 이후에 실행한다.

이제 DeltaTime과 클라이언트 서버 간의 핑 타임을 고려해서 외삽 방식으로 예측을 하도록 구성되어있다.

이후 예측된 값과 Target값의 차이 계산을 수행한다.

차이가 의미있는 값이라면 다음과 같은 ACCUMULATE ERROR라고 한다.

에러가 진짜인지 아닌지 측정할 때 현재 위치와 이전 위치를 측정해서 내적값을 구하고
방향성이 일치하는지 Test를 수행한다. 진짜로 에러라고 판정되면 

AccumulatedErrorSeconds에 DeltaSeconds를 넣어서 해당 값을 증가시켜준다.

AccumulatedErrorSeconds의 값이 지정한 값보다 크다면 bHardSnap을 true로 만든다.

해당 값이 true면 강제로 조정하는 작업과 Warning 출력 수행한다.

 

에러가 나지 않았다면

현재 Tick에서 설정할 Velocity, Losition, 각에 대한 값을 Lerp를 사용해서
현재 프레임에서 바꿔야할 값을 지정한다.

 


Simulated Proxy 움직임 리플리케이션

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine

언리얼 공식 문서이다.

아래로 내려가면 SimulatedProxy Replication과 관련된 내용이 있다.

 

간략하게 설명하자면

Simulated Proxy는 입력을 고려할 필요가 없기 때문에 프로세스가 매우 단순하게 계산한다.

움직임 물리를 적용하지 않고 서버로부터 움직임 업데이트를 받으면 캐릭터에 관련된 중요한 설정만 적용하고

몇가지 추가 프로세스를 통해 움직임을 더 부드럽고 사실적으로 만든다.

Simulated Proxy Movement Tick

SimulatedProxy가 Movement Tick에 SimulatedTick을 호출하여 시뮬레이션 하는 Movement를 위한 로직을 수행한다.

SimulatedTick은 계속 가장 최근에 제공된 Replicate된 Movement Data에 따라 움직인다.

Movememt Physics를 사용하는 경우 SimulateMovement함수 호출 후 SmoothClientPosition을 통한
네트워크 스무딩을 수행한다. 

Simulated Proxy Movement 순서
네트워크 스무딩

 

SimulatedTick()

Simulated Proxy의 캐릭터가 처리하는 캐릭터 움직임

  • 캐릭터가 Simulated Proxy일 경우에 추가적인 작업(SimulateMovement)을 수행한다.
  • 시뮬레이션으로 캡슐(RootComp)을 이동시킨 후 mesh의 움직임을 부드럽게 보간하는 SmoothClientPosition 작업 수행
더보기

CharacterMovementComponent의 TickComponent()에서
SimulatedProxy일 경우 SimulatedTick을 호출하는 것을 확인가능하다.

 

SimulateMovement 함수 호출을 통해 일반적인 캐릭터의 움직임이 추가적으로 동기화된다.

그리고 SmoothClientPosition을 통해 비주얼을 담당하는 MeshComponent를 CapsuleComponent의 위치로 부드럽게 이동시키는 작업이다.
해당 함수는 Proxy로 복제하는 Actor에 대해서만 진행하도록 체크한다.