< [UE5] Networked Movement in the Character Movement Component

언리얼엔진5/각종 지식

[UE5] Networked Movement in the Character Movement Component

Rocketbabydolls 2024. 5. 13. 17:07

캐릭터 무브먼트 컴포넌트는 걷기, 낙하, 수영, 비행 등 휴머노이드 캐릭터의 일반적인 이동 모드가 포함된 캡슐화된 이동 시스템을 제공하는 액터 컴포넌트입니다. 캐릭터 무브먼트 컴포넌트는 강력한 네트워크 게임플레이 통합 기능도 제공합니다. 기본 이동 모드는 모두 기본적으로 리플리케이트되도록 제작되었으며, 개발자가 커스텀 네트워크 이동을 만들 수 있는 프레임워크를 제공합니다.

 

캐릭터 무브먼트의 기본


UCharacterMovementComponent 는 ACharacter 액터 클래스 및 거기서 파생된 모든 블루프린트에 프리태치되어 제공됩니다.

TickComponent 함수 도중 UCharacterMovementComponent 는 PerformMovement 를 호출하여 현재 사용중인 이동 모드와 플레이어의 입력 변수에 따라 월드 내 원하는 가속도를 계산하며, 일반적으로 APlayerController 의 컨트롤 입력 변수로 표현됩니다. 이동 계산이 완료되면 UCharacterMovementComponent 는 소유 캐릭터에 최종 이동을 적용합니다.

ACharacter는 APawn에서 파생되었지만, 캐릭터는 단순히 캐릭터 이동 컴포넌트가 추가된 폰이 아닙니다. ACharacter는 여러 리플리케이트된 변수와 함수를 오버라이드하여 UCharacterMovementComponent에서 리플리케이션을 용이하게 하므로, UCharacterMovementComponent와 ACharacter는 함께 사용하도록 설계되었습니다.

 

PerformMovement 및 이동 물리


PerformMovement 함수는 게임 월드에서 캐릭터의 물리적인 움직임을 담당합니다. 비네트워크 게임에서는 UCharacterMovementComponent가 매 틱마다 PerformMovement를 직접 호출합니다. 네트워크 게임에서는 서버와 클라이언트용 특수 함수가 PerformMovement를 호출하여 플레이어의 로컬 머신에서 초기 이동을 수행하거나 원격 머신에서 해당 이동을 재생산합니다.

PerformMovement는 다음을 처리합니다:



임펄스, 힘, 중력 등 외부 피직스를 적용합니다.
애니메이션 루트 모션과 루트 모션 소스에서 움직임을 계산합니다.
캐릭터가 사용 중인 이동 모드에 따라 Phys* 함수를 선택하는 StartNewPhysics를 호출합니다.
각 이동 모드에는 속도와 가속도 계산을 담당하는 자체 Phys* 함수가 있습니다. 예를 들어, PhysWalking은 지상에서 이동할 때 캐릭터의 이동 피직스를 결정하고, PhysFalling은 공중에서 어떻게 동작할지 결정합니다. 이러한 동작의 세부 사항을 디버깅하려면 각 함수의 내부를 살펴봐야 합니다.

틱 중에 캐릭터가 떨어지기 시작하거나 물체와 충돌하는 등 이동 모드가 변경되면 Phys* 함수는 StartNewPhysics를 다시 호출하여 새로운 이동 모드에서 캐릭터의 동작을 계속합니다. StartNewPhysics와 Phys* 함수는 각각 발생한 StartNewPhysics의 반복 횟수를 전달합니다. 이 재귀가 허용되는 최대 횟수는 MaxSimulationIterations 파라미터입니다.

 

무브먼트 리플리케이션 요약


UCharacterMovementComponent는 소유자의 네트워크 역할을 사용하여 이동을 리플리케이트하는 방법을 결정합니다. 세 가지 네트워크 역할은 다음과 같습니다:


네트워크 역할 설명
자율 프록시 - 캐릭터가 소유 클라이언트의 머신에 있으며 플레이어가 로컬로 제어합니다.
권한 - 캐릭터가 게임을 호스팅하는 서버에 존재합니다.
시뮬레이션 프록시 - 캐릭터가 서버의 AI에 의해 제어되든, 다른 클라이언트의 자율 프록시에 의해 제어되든 원격 제어된 캐릭터를 볼 수 있는 다른 클라이언트에 존재합니다.
복제 프로세스는 매 틱마다 반복되는 TickComponent 함수 내의 사이클을 따릅니다. 캐릭터가 움직임을 수행하면 네트워크 게임의 모든 다른 머신에 있는 복사본이 서로 원격 프로시저 호출(RPC)을 통해 움직임 정보를 동기화하며, 각기 다른 네트워크 역할에 따라 적절하게 다른 실행 경로를 사용합니다.

아래 표는 이 과정에서 각 머신에서 UCharacterMovementComponent가 수행하는 작업에 대한 단계별 개요를 제공합니다:

 

자율 프록시(소유 플레이어의 클라이언트)


1 소유 클라이언트는 로컬에서 자율 프록시를 제어합니다. PerformMovement 는 무브먼트 컴포넌트의 물리적 이동 로직을 실행합니다.
2 프록시는 방금 이동한 방법에 대한 데이터가 들어있는 FSavedMove_Character 를 빌드한 다음 SavedMoves 에 큐에 대기시킵니다.
3 유사한 FSavedMove 항목은 함께 결합됩니다. 자율 프록시는 ServerMove RPC를 통해 데이터의 압축 버전을 서버로 전송합니다.

 

권한 있는 액터(서버)
4 서버는 ServerMove를 수신하고 PerformMovement를 사용하여 클라이언트의 움직임을 재현합니다.
5 서버는 서버이동 후 자신의 위치가 클라이언트가 보고한 최종 위치와 일치하는지 확인합니다.
6 서버와 클라이언트의 최종 위치가 일치하면 이동이 유효하다는 신호를 클라이언트에 다시 보냅니다. 그렇지 않으면 ClientAdjustPosition RPC로 정정 신호를 보냅니다.
7 서버는 ReplicatedMovement 구조를 복제하여 연결된 다른 클라이언트의 시뮬레이션된 프록시에 위치, 회전, 현재 상태를 전송합니다.

 

 

자율 프록시(소유 플레이어의 클라이언트)
8 클라이언트가 ClientAdjustPosition을 받으면 서버의 이동을 재현하고 저장된 이동 대기열을 사용하여 단계를 다시 추적하여 새로운 최종 위치를 얻습니다. 이동이 성공적으로 해결되면 저장된 이동을 대기열에서 제거합니다.
시뮬레이션된 프록시(다른 모든 클라이언트)
9 시뮬레이션된 프록시는 복제된 이동 정보를 직접 적용합니다. 네트워크 스무딩은 최종 모션에 대한 시각적 정리를 제공합니다.

 

 

이 프로세스는 네트워크 게임 내에서 세 가지 유형의 머신 간의 움직임을 동기화합니다. 특정 캐릭터를 제어하는 사용자는 서버의 간섭을 최소화하고 자신의 캐릭터를 로컬에서 제어하고 있다는 착각을 유지해야 하며, 다른 사용자의 캐릭터가 자신의 컴퓨터에서 수행하는 동작과 유사한 동작을 수행하는 것을 볼 수 있어야 합니다.

이 프로세스의 복잡성 대부분은 플레이어가 자신의 캐릭터를 최대한 원활하게 제어할 수 있도록 자율 프록시와 서버의 프록시 간의 예측과 수정을 중재하는 데 중점을 둡니다. 이에 비해 시뮬레이션 프록시는 서버가 지정한 위치에서 최신 상태를 유지하기만 하면 됩니다.

 

 

시뮬레이션 프록시로 이동 복제하기
소유자가 아닌 클라이언트 머신의 캐릭터는 자율 프록시가 아닌 시뮬레이션 프록시입니다. 시뮬레이션 프록시는 서버에 응답하는 일만 하기 때문에 서버에서 시뮬레이션 프록시로 움직임을 복제하는 프로세스는 매우 단순화되어 있습니다. 이동 물리학을 시뮬레이션하는 대신 서버로부터 이동 업데이트를 받으면 서버가 말하는 대로 위치, 회전, 속도를 설정하고 몇 가지 추가 프로세스를 통해 더 부드럽고 사실적인 이동을 구현합니다.

 

리플리케이트된 무브먼트 정보 저장
액터가 무브먼트를 리플리케이트할 때는 트랜스폼을 직접 리플리케이트하지 않습니다. 그 대신, 모든 액터는 ReplicatedMovement 라는 리플리케이트된 변수를 유지하며, 이 변수는 FRepMovement 구조를 사용합니다.

블루프린트에서 Replicate Movement 변수로 표현되는 부울 bReplicateMovement는 액터가 이 구조에 이동 정보를 저장하고 클라이언트에 리플리케이트하도록 플래그를 지정합니다. 클라이언트가 ReplicatedMovement에 대한 업데이트를 받으면, RepNotify 함수는 저장된 이동 데이터의 압축을 풀고 그에 따라 액터의 위치와 속도에 대한 업데이트를 실행하는 OnRep_ReplicatedMovement 를 호출합니다.

블루프린트 내부에서는 ReplicatedMovement나 그 OnRep에 액세스할 수 없지만, C++에서 OnRep_ReplicatedMovement를 오버라이드할 수 있으며, ReplicatedMovement의 리플리케이션 조건은 GetLifetimeReplicatedProps 에서도 오버라이드할 수 있습니다. 이를 통해 C++ 기반 액터 클래스에서 무브먼트 리플리케이션 작동 방식을 커스터마이징할 수 있습니다.

ACharacter 에서 ReplicatedMovement 구조체는 시뮬레이션된 프록시에 대해서만 리플리케이트됩니다. 그렇지 않으면 서버 이동 및 클라이언트 조정 RPC를 사용하여 이동을 처리하는 자율 프록시에서는 무시됩니다.

 

캐릭터가 다른 액터를 베이스로 사용하는 경우, 클라이언트가 서버에 따라 올바르게 베이스가 되는지 확인하기 위해 추가 로직을 적용하는 ReplicatedBasedMovement를 대신 사용합니다. 캐릭터가 루트 모션 시스템을 사용하는 경우, 이 모든 프로세스는 무시되고 RepRootMotion이 사용됩니다.

 

시뮬레이션 프록시에서 움직임 틱하기
UCharacterMovementComponent가 시뮬레이션된 프록시에서 TickComponent를 실행하면 SimulatedTick을 호출하여 움직임 시뮬레이션 로직을 처리합니다. 이 경우 위에서 설명한 리플리케이트된 움직임은 수행되지 않습니다. 대신 SimulatedTick은 가장 최근에 제공된 복제된 움직임 데이터에 따라 계속 움직입니다. 표준 이동 물리학을 수행할 때는 SimulateMovement 함수를 호출한 다음 SmoothClientPosition으로 최종 검증 및 네트워크 평활화를 수행합니다.

 

시뮬레이션된 움직임 수행
SimulateMovement 함수는 시뮬레이션된 프록시 캐릭터의 움직임을 담당합니다. SimulatedTick에 의해 호출될 뿐만 아니라 OnRep_ReplicateMovement에 의해서도 호출됩니다. 이 함수는 다음 프로세스를 수행합니다:

1. 소유 캐릭터의 GetReplicatedMovement 함수를 호출하여 ReplicatedMovement에 대한 참조를 가져옵니다.
2. 안전 검사를 수행하여 리플리케이트된 모션 데이터가 유효한지, 클라이언트의 베이스가 해결되었는지 확인합니다.
3. 네트워크 업데이트가 수신되었는지 확인합니다.
4. GetReplicatedMovementMode로 얻은 서버의 캐릭터 이동 모드를 적용합니다.
5. 네트워크 업데이트와 관련된 모든 플래그를 리셋합니다.
6. 현재 MovementMode와 캐릭터의 현재 상태에 대한 정보를 기반으로 시뮬레이션된 이동에 대한 로직을 수행합니다.

 

 

네트워크 스무딩
단순히 캐릭터의 위치와 회전을 복제하여 움직임을 복제하면 캐릭터가 몇 초마다 순간 이동하는 것처럼 보일 것입니다. 이는 로컬 머신의 렌더링 속도가 네트워크를 통해 데이터를 전송하는 속도보다 빠르기 때문입니다. 예를 들어 클라이언트가 240Hz 재생률로 모니터에 렌더링하고 있지만 복제된 움직임은 30Hz로만 전송될 수 있습니다.

네트워크 스무딩은 이 동작을 부드럽게 처리하는 프로세스로, 캐릭터를 대상에 즉시 스냅하는 대신 소스 위치에서 대상 위치로 점진적으로 보간합니다. 소스 위치는 캐릭터의 현재 위치로, 타깃 위치는 클라이언트 예측 데이터로 지정됩니다. 보간 자체는 네트워크 스무딩 모드를 사용하여 어떤 종류의 보간을 사용할지 결정하는 SmoothClientPosition에서 처리됩니다.