나만의 작은 도서관

[TIL][C++] 251021 MMO 서버 개발 119일차: [언리얼] 보간(Interpolation) 함수에 대해 알아보자, CMC의 DisableMovement()의 진실? 본문

Today I Learn

[TIL][C++] 251021 MMO 서버 개발 119일차: [언리얼] 보간(Interpolation) 함수에 대해 알아보자, CMC의 DisableMovement()의 진실?

pledge24 2025. 10. 21. 22:21
주의사항: 해당 글은 일기와 같은 기록용으로, 다듬지 않은 날것 그대로인 글입니다. 

[언리얼] 보간(Interpolation) 함수에 대해 알아보자

  • 보간(Interpolation)이란 알려진 두 점 사이에 위치한 점의 값을 예측하거나 추정하는 기법을 의미한다.
  • 언리얼에서 보간은 두 지점의 중간 위치를 구할 때나 두 회전값의 중간 회전값을 구할 때 유용하게 쓰이며, 보간을 하기 위한 함수들이 다양하게 제공되고 있다.

 

FMath::Lerp

  • 선형 보간(Linear Interpolation) 방식이며, 인자로 넣어주는 alpha 값으로 보간 계수를 설정한다.
출발지(Src): 0
목적지(Dst): 100
보간 계수(Alpha): 0.3

=> 보간 위치는 30

FMath::FInterpTo / VInterpTo / RInterpTo

  • 감쇠율(Damping Rate)을 기반으로 한 보간 방식이며, 지수적(expotential)으로 목적지에 가까워진다. 인자로 넣어주는 InterpSpeed 값으로 감쇠율의 배수를 설정할 수 있다.
    • 즉, DeltaTime * InterpSpeed은 Alpha가 된다.
  • UE5의 InterpTo 계열의 함수들인 이 세 함수는 인자로 총 4개를 받으며, 각각 Current, Target, DeltaTime, InterpSpeed가 된다. 보간공식은 아래와 같다.
New Value = Current Value + (Target Value - Current Value) * (DeltaTime * Interp Speed)

 

 

감쇠율이 보간에 적용되는 방식의 예

  • 쉽게 생각하면 공을 땅바닥에 떨어뜨렸을 때 점점 높이가 줄어드는 걸 생각하면 된다. 감쇠율이 20%이고 공의 처음 위치가 100일 때 다음 위치는 80, 그다음 위치는 64가 된다. 이를 공식을 적용하여 정리하면 아래와 같다.
출발지: 100 | 목적지: 0

공의 높이: 100
공의 높이: 100 + (0 - 100) * 0.2 = 80
공의 높이: 80 + (0 - 80) * 0.2 = 64
공의 높이: 64 + (0 - 64) * 0.2 = 51.2
========================================
목적지에 가까워진 거리
20 -> 16 -> 12.8
=> 지수적으로 가까워진다!

 

 

FInterpTo 계열의 보간 함수는 Tick 환경에 맞게끔 설계된 함수들

  • 인자로 DeltaTime을 받는 것을 보면 알겠지만, InterpTo 계열의 보간 함수들은 Tick함수에 넣어서 사용하게끔 설계된 함수들이다. 이는 각 클라이언트가 프레임에 구애받지 않고 동일한 보간 속도를 적용하는데 용이하게 한다.

 

 

각 함수를 구분하는 방법?

  • InterpTo 계열의 보간 함수의 이름을 보면 접두사만 다르고 이름이 같은데, 각각의 접두사 의미를 알고 있으면 구분이 쉽다. 의미는 아래와 같다.
    • FInterTo의 ‘F’ = Float. 그래서 Src, Dst의 인자 타입은 Float이다.
    • VInterTo의 ‘V’ = Vector. 그래서 Src, Dst의 인자 타입은 Vector이다.
    • RInterTo의 ‘R’ = Rotator. 그래서 Src, Dst의 인자 타입은 Rotator이다.

FMath::FInterpConstantTo / VInterpConstantTo / RInterpConstantTo

  • InterpConstantTo 계열의 보간 함수는 동일한 속도로 보간하는 함수들로, 인자로 넣어주는 InterpSpeed자체가 초당 이동량이 된다.
  • UE5의 InterpTo 계열의 함수들인 이 세 함수는 인자로 총 4개를 받으며, 각각 Current, Target, DeltaTime, InterpSpeed가 된다.
Current = 0
Target = 100
InterpSpeed = 50
DeltaTime = 0.0167 (≈ 60FPS)

Frame당 이동 = 50 * 0.0167 = 0.8335
→ 매 프레임 0.8335씩 증가, 약 2초 후 목표 도달
  • InterSpeed가 50, 즉, 초당 이동량이 50이므로 100만큼의 거리를 가는 데 걸리는 시간은 2초가 된다.



[언리얼] CMC의 DisableMovement()의 진실?

  • CMC::DisableMovement() 함수는 호출시 CMC가 처리하는 모든 이동을 중단하게 된다.

 

root motion과의 관계성

  • CMC를 이야기하다보면 root motion 이야기를 하게 된다. 만약 DisableMovement함수를 호출했다면, root motion을 활성화한 몽타주를 재생하는 경우 이동을 할까?
  • 결론을 말하자면 이동하지 않는다. 그 이유는 DisableMovement() 함수의 내부를 보면 알 수 있는데, 그 내부는 아래와 같다.
void UCharacterMovementComponent::DisableMovement()
{
	if (CharacterOwner)
	{
		SetMovementMode(MOVE_None);
	}
	else
	{
		MovementMode = MOVE_None;
		CustomMovementMode = 0;
	}
}
  • 정말 별 거 없다. 그저 캐릭터의 MovementMode를 Move_None으로 바꿔주기만 한다. MOVE_None이 되면 해당 캐릭터는 절대 움직이지 않기 때문에 root motion이더라도 움직이지 않는 것이다. 세부적인 동작 과정을 나열하자면 아래와 같다.

 

세부 동작

  • DisableMovement() → MovementMode = MOVE_None으로 설정됨.
    • → 캐릭터 이동 속도 계산과 위치 갱신(물리기반 이동)은 멈춤.
    • → Velocity는 0으로 유지되고 Tick 시 위치 업데이트도 안 함.
  • 하지만 Root Motion은 AnimInstance 단에서 계산되어 USkeletalMeshComponent::TickPose()에서 발생. 이때:
    • Root Motion이 활성화되어 있다면(bRootMotionMode != IgnoreRootMotion),
    • UCharacterMovementComponent는 여전히 ConsumeRootMotion()을 호출하여 Root Motion Transform을 받아서 처리하려고 함.
  • 그러나 MovementMode == MOVE_None 상태에서는 실제 이동 적용이 제한됨.
  • 즉, 루트모션의 Transform 데이터는 계산되지만 위치 반영이 안 된다.
항목 상태 설명
Root Motion 계산 유지됨 애니메이션 블렌딩과 루트 본 이동은 계속 계산됨
캐릭터 실제 이동(Transform 변경) 중단됨 CMC가 MOVE_None 상태라 이동 반영 안 됨
Velocity 0 물리 이동 정지
Root Motion 애니메이션 자체 재생됨 시각적으로는 움직이는 것처럼 보이지만 실제 위치는 안 바뀜

 

 

EnableMovement()가 없는데 어떻게 되돌릴까?

  • 왜인지는 모르겠지만 UE5에는 DisableMovement() 함수는 있으면서 반대인 EnableMovement() 함수는 없다. 그럼 DisableMovement()를 호출하면 되돌릴 수 없는 건가?
  • 위에서 내부 로직을 봐서 알겠지만 그저 SetMovementMode에 Move_None으로 인자를 넣어서 호출할 뿐이므로, 움직이게 하고 싶다면 Move_Walking 같은 걸 넣어주면 된다.
// 이동 비활성화
CharacterMovement->DisableMovement();

// 일정 조건 후 이동 재활성화
CharacterMovement->SetMovementMode(MOVE_Walking);
// 또는
CharacterMovement->SetMovementMode(EMovementMode::MOVE_Walking);