프로젝트 회고 / / 2025. 12. 2. 22:53

[UE5 액션] 애니메이션 라이프 사이클 기반 비동기 상태 관리

반응형

🎮 구현 목표

전투 중 입력으로 스킬을 캔슬하거나 피격으로 몽타주가 중단되는 등, 몽타주가 끝까지 재생되지 않는 상황에서도 캐릭터 상태가 정상적으로 복구되는 전투 시스템을 구현하고자 했습니다.

 

🚨 문제 상황

 

몽타주가 중단되면 Gameplay Tag가 제거되지 않는 현상 발생

기존 구조에서는 공격/피격 시작 시 상태 태그를 추가하지만, 종료 시 태그 제거는 AnimNotify에 의존했습니다.

 

AnimNotify의 치명적인 한계:

  • 몽타주가 중단되면 해당 프레임의 Notify가 호출되지 않음
  • 블렌드 아웃 발생 시 몽타주 끝부분 Notify가 누락될 위험

즉, 몽타주가 끝까지 재생되고 블렌드 없이 종료된다는 보장이 있어야만 AnimNotify로 상태 관리가 가능합니다.

실제 발생한 문제:

  • 공격 중 회피 → TAG_Character_State_Attacking 미제거 → 캐릭터 영구 이동 불가, 공격 입력 무시
  • 피격으로 공격 중단 → TAG_Character_State_Attacking 미제거 → 캐릭터 영구 이동 불가, 공격 입력 무시

 

💭 해결 방법

 

"태그 추가를 호출 로직에서 했으면, 제거도 호출 로직에서 하면 되지 않을까?"

핵심은 몽타주 종료 시점에 무조건 호출되는 콜백을 찾는 것이었습니다.

 

이 때 알게된 것이 AnimInstance에 있는 FOnMontageEndedDelegate였습니다. 몽타주가 재생되고나면 어떤 이유에서든 몽타주가 종료될 때 호출을 보장해줍니다. 심지어 두 번째 매개변수로 중단 여부까지 확인할 수 있으니 정상적으로 재생이 끝났을 때와 중단되었을 때의 로직 처리를 구분하여 로직을 처리하기도 수월했습니다.

 

고려한 방법들:

  1. Timer 사용 → 몽타주 길이만큼 타이머 설정 후 태그 제거
    • 리스크: 몽타주가 일찍 중단되어도 타이머는 계속 진행
    • 판단: 근본적 해결 아님 ❌
  1. FOnMontageEnded Delegate 활용
    • 몽타주가 어떤 이유로든 종료되면 무조건 호출
    • bInterrupted 파라미터로 정상 종료/중단 여부 구분 가능
    • UE5가 제공하는 안전한 방법

 

🔧 구현

피격 시스템에 먼저 적용하여 검증:

void ASoulCharacterBase::HitReaction(AActor* Attacker, UDamageType* DamageType, 
            const FVector& HitDirection)
{
    // ...
    FOnMontageEnded OnMontageEnded;
    OnMontageEnded.BindUObject(this, &ThisClass::RecoveryHitReaction);
    AnimInstance->Montage_Play(HitReactAnimation);
    AnimInstance->Montage_SetEndDelegate(OnMontageEnded, HitReactAnimation);
}
void ASoulCharacterBase::RecoveryHitReaction(UAnimMontage* AnimMontage, bool bInterrupted)
{
    check(StateComponent);

    RemoveState(SoulGameplayTag::Character_State_Hit);
    RemoveState(SoulGameplayTag::Character_State_Down);
    RemoveState(SoulGameplayTag::Character_State_Attacking);
    RemoveState(SoulGameplayTag::Character_State_Attacking_Recovery);

    StateComponent->ToggleMovementInput(true);
    AttributeComponent->ToggleStaminaRegeneration(true, 0.5f);
}

검증 결과:

  • ✅ 피격 모션 중 공격받아 중단 → 상태 정상 복구
  • ✅ 피격 모션 정상 종료 → 상태 정상 복구
  • ✅ 블렌드 아웃 발생 → Delegate 정상 호출

모든 태그 제거 로직을 FOnMontageEndedDelegate를 통하도록 변경했습니다.

 

✅ 결과

안정적인 상태 관리 시스템 구축

모든 종료 케이스 대응

  • 정상 종료, 입력 캔슬, 피격 중단, 블렌드 아웃 모두 처리

상태 불일치 문제 완전 해결

  • 공격 캔슬 후 이동 불가 버그 소멸
  • 스태미나 재생 미복구 현상 해결

확장 가능한 구조

  • bInterrupted로 종료 상황별 로직 분기 가능
  • 새로운 몽타주 추가 시 동일 패턴 적용

AnimNotify특정 타이밍의 이벤트(이펙트 재생, 사운드 등)에 적합하지만, 상태 초기화처럼 반드시 실행되어야 하는 로직은 Delegate로 보장해야 합니다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유