[Mover] WalkingMode에서 step-up 성공 직후 같은 틱에 낙하 전환되면 캐릭터 속도가 폭발(런치)함

요약

Mover로 보행 중인 캐릭터가 step-up에 성공하고 같은 시뮬레이션 틱에서 walkable 바닥을 찾지 못해 Falling으로 전환되면, 낙하 모드로 넘겨지는 속도가 비정상적으로 커져 캐릭터가 공중으로 튕겨 나갑니다. 분석 결과 UWalkingMode::SimulationTick_Implementation의 step-up 성공 경로에서 PercentTimeAppliedSoFar를 갱신하지 않는 것이 원인으로 보입니다.

원인 분석 (5.7.4 기준)

  1. 출력 속도 = RelevantMoveDelta / TotalDeltaSeconds — FMovementRecord::GetRelevantVelocity() (MovementRecord.h ~50). CaptureFinalState가 이 값을 액터 속도로 기록.
  2. step-up 성공 시 수평 전진량(forward/slide)은 relevant로 적립(분자 증가), up/down은 non-relevant — UGroundMovementUtils::TryMoveToStepUp (GroundMovementUtils.cpp ~153/205/214/333).
  3. PercentTimeAppliedSoFar는 초기 이동(~188)·램프(~201)·step-up 실패 시 슬라이드(~221) 등 모든 경로에서 갱신되지만, if (!TryMoveToStepUp(…))(~215)에 else가 없어 step-up 성공 시에는 갱신되지 않음.
  4. 직후 바닥이 walkable이 아니면(~243) Falling 전환(~246–249)
RemainingMs   = StepMs - StepMs * PercentTimeAppliedSoFar;
SetDeltaSeconds((StepMs - RemainingMs) * 0.001f);   // = DeltaSeconds * PercentTimeAppliedSoFar

PercentTimeAppliedSoFar 가 작은 값(예: ~0.02)으로 남아 있으면 분모가 0에 가까워져 속도가 약 50배로 폭발.

실측 로그(step-up 성공 지점, 보정 전 값):

PercentTimeAppliedSoFar = 0.019378 , StepUpFraction = 0.980622

PercentTimeAppliedSoFar = 0.028127 , StepUpFraction = 0.971873

→ step-up이 남은 이동분을 거의 전부 전진했는데도 분모는 작은 값 그대로라 폭발.

근거 — async 버전은 이미 처리함

동일 알고리즘인 UAsyncWalkingMode::SimulationTick_Implementation (AsyncWalkingMode.cpp)은 step-up 성공 시 명시적으로 갱신합니다:

if (UGroundMovementUtils::TestMoveToStepOver(...)) {   // 성공
    PercentTimeAppliedSoFar = 1.0f;                    // ~243
}

그리고 동일한 Falling 전환 공식(~287–288)을 사용합니다. 두 구현이 step-up 성공 경로에서만 갈라지며, async에는 갱신이 있고 sync에는 없다는 점에서 동기 WalkingMode의 누락(버그)으로 판단됩니다.

임시 수정 (확인용)

  • UWalkingMode::SimulationTick_Implementation 에서 UGroundMovementUtils::TryMoveToStepUp 성공 시, 처리​

if에 else를 추가해 step-up이 실제 전진한 수평 거리 비율만큼 보정하면 런치가 사라집니다:

else { // step-up 성공
    const float StepUpHoriz   = FVector::VectorPlaneProject(UpdatedComponent->GetComponentLocation() - PreStepUpLocation, UpDirection).Size();
    const float IntendedHoriz = FVector::VectorPlaneProject(OrigMoveDelta, UpDirection).Size();
    const float StepUpPct      = (IntendedHoriz > UE_KINDA_SMALL_NUMBER) ? (StepUpHoriz / IntendedHoriz) : (1.f - PercentTimeAppliedSoFar);
    PercentTimeAppliedSoFar    = FMath::Clamp(PercentTimeAppliedSoFar + StepUpPct, 0.f, 1.f);
}

(async처럼 = 1.0f로 둬도 폭발은 사라지나, 부분 step-up에서는 속도를 과소평가함.)

질문

  1. 재현 가능한지, 그리고 기존에 확인된 이슈인지(트래킹 번호 / UE-XXXXX)?
  2. UWalkingMode의 step-up 성공 경로에서 PercentTimeAppliedSoFar 미갱신이 버그가 맞는지?
  3. UWalkingMode(미갱신)와 UAsyncWalkingMode(= 1.0f)의 차이가 의도된 것인지, 동기 경로의 누락인지?
  4. 권장 수정 방향은? (async와 동일하게 = 1.0f vs 실제 수평 이동 비율 보정)
    [Attachment Removed]
  1. 레벨에 계단 메시 배치
  2. 계단 콜리전을 Use Complex Collision As Simple(메시를 그대로 따르는 per-poly 콜리전)로 설정
  3. 기본 WalkingMode를 쓰는 Mover 캐릭터로 계단 모서리를 향해 달려서 올라감
  4. 모서리 충돌 시 간헐적으로 캐릭터가 큰 속도로 런치됨
  5. 기대: 정상 보행 속도로 계단을 오르거나, 모서리에서 이탈 시 정상 속도로 Falling 진입
    1. 참고: Complex 콜리전에서 특히 잘 재현됨. 날카로운 모서리가 (a) 서브스텝 초반에 블로킹 히트를 만들어 PercentTimeAppliedSoFar가 매우 작게 남고, (b) step-up 직후 모서리에서의 바닥 스윕이 walkable 판정에 실패해 같은 틱에 낙하 전환되기 때문으로 추정. 단순 박스 콜리전에서는 바닥이 안정적으로 잡혀 재현되지 않음.
      [Attachment Removed]

안녕하세요.

코드 상 잔여 PercentTimeAppliedSoFar 값을 사용하는 부분이 없고 관련 이슈가 발생하는 것으로 보아 AsyncWalkingMode와의 불일치 버그인 것으로 판단되며, AsyncWalkingMode에 PercentTimeAppliedSoFar 값을 1로 설정하는 변경사항은 CL 38638891 에서 다른 이슈를 수정하면서 발생한 불일치인 것으로 확인됩니다.

현재 언리얼 엔진 5.8에서 해당 이슈가 수정되어 WalkingMode와 AsyncWalking 모두 유사한 방식으로 변경되었으며 추후 5.8 버전을 사용하시거나 아래 커밋과 같이 수정하는 것을 권장드립니다.

CL 50999505

https://github.com/EpicGames/UnrealEngine/commit/609e197a6c3cbb9ee999408d78b75f0469c3dcea

감사합니다.

[Attachment Removed]