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

캐릭터 Movement 추가 예시 - Unreal Network MultiPlayer Framework

by daisy0461 2025. 3. 12.

이번 글에선 CharcterMovementComponent를 상속해서 텔레포트를 만들어 볼 것이다.

CharacterMovementComponent를 상속해서 만드는 이유는

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/character-movement-component?application_version=4.27

해당 공식문서의 아래쪽에

이 토픽에서 다른 방법으로 구현했을 때의 단점을 살펴보면 된다.

 

이번 구현에서는

CharacterMovmentComponent가 사용하는 RPC기능에 텔레포 기능을 추가 선언하고
몇몇 함수를 override하여 텔레포트를 구현해볼 것이다.

 

Teleport IA를 만들어주고 InputAction도 추가한다.

이제 캐릭터 h파일에서 다음과 같이 선언하고

생성자에서도 추가와 EnhancedInput과 바인딩해준다.

다음과 같이 입력설정을 완료한다.

 

부모 클래스인 Character에서 생성한 CharacterMovement를 Teleport를 추가한 ABCharacterMovementComponent로 교체해야한다.

이를 위해서 생성자를 인자가 있는 생성자로 변경을 해줘야한다.

ACharacter의 생성자를 보면 인자를 하나 받는데 기본값이 있기에 그대로 호출할 수 있었다.

다음과 같이 사용하는 Character의 생성자에 인자를 추가해주면 된다.

cpp에는 다음과 같이 기본적인 CharacterMovementComponent라고 이름이 있는 Subobject의 형을
UABCharacterMovementComponent라고 바꿔라고 Initializer에 명령할 수 있다.

실행을 해보면 CharacterMovement가 ABCharacter Movement Component로 교체된 것을 확인할 수 있다.

ABCharacter Movement Component에 간단한 teleport를 제작해준다.

여기서 OnMovementUpdated 주석을 보면 실제로 움직이는 PerformMovement()에서 실행하는 함수이며

원래 함수에는 아무것도 구현되어있지 않아 사용자가 override해서 사용하도록 한 함수인 것을 확인할 수 있다.

 

Player에서 ABCharacterMovementCoponent를 받아서 ABTeleport를 실행해준다.

 

이렇게하면 서버의 Player는 쿨타임을 가지고 Teleport를 한다. 

 

클라이언트 Teleport

클라이언트 Player에서 Teleport를 실행한다.

ABCharacterMovementComponent로 가서 Teleport가 눌러졌다는 값을 true로 한다.

OnMovementUpdate는 TickComponent() -> SimulatedTick() -> SimulatedMovement가 호출되고 
SimulatedMovement에서 OnMovementUpdate가 호출된다.
즉, OnMovementUpdate는 CharacterMovementComponent에서 매 틱마다 실행되기 때문에
bPressedTeleport와 bDidTeleport의 값이 통과하면 바로 ABTeleport()를 실행한다.

ABTeleport에서 TeleportTo를 통해 서버보다 클라이언트가 먼저 텔레포트(이동)한다.

클라이언트에서 텔레포트로 인해 이동이 발생하면 FABSaveMove를 생성한다.

FABSaveMove_Character는 클라이언트에서 발생한 이동을 저장하고 이후 서버에 전송하여 동기화에 사용된다.

SetInitialPosition을 통해 현재 bPressedTeleport와 bDidTeleport 상태를 디코딩하여 FABSaveMove_Character에 저장한다.

클라이언트가 FABSaveMove_Character를 전송하기 전에 예측 데이터가 있는지 확인한다.

클라이언트가 예측 데이터를 관리하는 ClientPredictionData를 들고온다.

만약 nullptr이라면 새로운 객체를 생성한다.

 

그렇다면 왜 새로운 객체를 생성할까?

렉 때문에 클라이언트는 입력을 바로 서버로 보내는 것이 아니라 ClientPredictionData를 기반으로 서버가 승인할 것으로 예상되는 이동을 수행한다.

서버는 응답을 받으면 해당 ClientPredictionData를 그대로 받아들이거나 보정한다.

즉, SaveMove는 단순히 데이터를 저장하고 보내지만 클라이언트 예측 및 서버 보정은 ClientPredictionData가 담당하기에 만들어줘야한다.

 

이제 서버에서 UpdateFromCompressedFlags를 수행한다.

클라이언트에서 보낸 데이터가 알맞으면 서버에서도 Teleport를 수행한다.

 

이후 서버가 결정한 최종적인 캐릭터 위치 데이터를 클라이언트로 전송하여 동기화한다.