영상을 불러오지 못했습니다

UE4 ACharacter::PlayAnimMontage 반환값이 실제 재생시간과 다른 이유

807 단어
4 분
UE4 ACharacter::PlayAnimMontage 반환값이 실제 재생시간과 다른 이유
UE4 ACharacter::PlayAnimMontage 반환값이 실제 재생시간과 다른 이유

이 글은 2021년 5월의 Unreal Engine 4.26 동작을 기준으로 검토해 옮겼다. UE 4.27.2 소스에서도 같은 호출 경로가 유지되는 것을 함께 확인했다.

Character에서 Animation Montage를 재생할 때 흔히 ACharacter::PlayAnimMontage를 사용한다.

/**
* Play Animation Montage on the character mesh.
* Returns the length of the animation montage in seconds,
* or 0.f if failed to play.
*/
UFUNCTION(BlueprintCallable, Category = Animation)
virtual float PlayAnimMontage(
class UAnimMontage* AnimMontage,
float InPlayRate = 1.f,
FName StartSectionName = NAME_None);

이 반환값은 “현재 설정으로 실제 재생되는 시간”이라고 해석하기 쉽다. 하지만 UE 4.26에서 이 함수는 InPlayRate, Montage의 RateScale, StartSectionName이 반영된 남은 시간을 반환하지 않았다.

내부에서는 기본 반환 형식을 사용한다#

당시 구현은 UAnimInstance::Montage_Play를 아래와 같이 호출했다.

float ACharacter::PlayAnimMontage(
UAnimMontage* AnimMontage,
float InPlayRate,
FName StartSectionName)
{
UAnimInstance* AnimInstance = Mesh ? Mesh->GetAnimInstance() : nullptr;
if (AnimMontage && AnimInstance)
{
const float Duration = AnimInstance->Montage_Play(
AnimMontage,
InPlayRate);
if (Duration > 0.f)
{
if (StartSectionName != NAME_None)
{
AnimInstance->Montage_JumpToSection(
StartSectionName,
AnimMontage);
}
return Duration;
}
}
return 0.f;
}

Montage_Play의 세 번째 인자는 아래 enum이며, 기본값은 MontageLength였다.

enum class EMontagePlayReturnType : uint8
{
MontageLength,
Duration,
};

ACharacter::PlayAnimMontage는 이 기본값을 그대로 사용해 MontageToPlay->SequenceLength를 반환한다. 재생 속도를 바꾸더라도 반환값은 원본 전체 길이다.

// UE 4.27.2의 Montage_Play 반환 부분을 단순화한 형태
return ReturnValueType == EMontagePlayReturnType::MontageLength
? MontageLength
: MontageLength / (InPlayRate * MontageToPlay->RateScale);

StartSectionName도 반환값을 계산한 뒤 Montage_JumpToSection으로 적용되므로, 특정 Section에서 시작해도 반환값은 전체 Montage 길이 그대로다.

속도가 반영된 전체 길이가 필요할 때#

Character wrapper 대신 Anim Instance의 함수를 직접 호출하고 반환 형식을 Duration으로 지정할 수 있다.

UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (!AnimInstance || !AnimMontage)
{
return 0.f;
}
const float Duration = AnimInstance->Montage_Play(
AnimMontage,
InPlayRate,
EMontagePlayReturnType::Duration);
if (Duration > 0.f && StartSectionName != NAME_None)
{
AnimInstance->Montage_JumpToSection(
StartSectionName,
AnimMontage);
}

이때 얻는 Duration도 전체 Montage를 처음부터 재생한다고 가정한 속도 보정 길이다. 시작 Section, Section 연결 관계, 반복 Section, runtime의 Montage_SetNextSection, blend out과 interruption까지 포함한 실제 종료 시점은 표현하지 못한다.

원문의 수식은 제한적으로만 맞는다#

원문에서는 다음 계산을 제안했다.

const float Duration =
PlayAnimMontage(AnimMontage, InPlayRate) /
(InPlayRate * AnimMontage->RateScale);

UE 4.26의 Montage_Play(..., EMontagePlayReturnType::Duration) 내부 계산과 같은 형태이므로, 전체 Montage가 순서대로 한 번 재생되는 단순한 경우에는 맞는다. 다만 아래 상황에서는 실제 종료시간이 아니다.

  • StartSectionName으로 중간에서 시작한다.
  • Section이 반복되거나 runtime에 다음 Section을 바꾼다.
  • 다른 Montage가 재생을 중단한다.
  • blend out이나 notify 시점을 기다리는 것이 목적이다.
  • 0 또는 비정상적인 Play Rate를 전달한다.

게임플레이 로직을 정확한 종료 시점에 연결하려면 float 길이로 timer를 예약하기보다 Montage 종료 delegate, Blueprint의 On Completed·On Blend Out·On Interrupted, Anim Notify를 사용한다. 길이 반환값은 재생 성공 여부와 단순한 예상 시간을 얻는 용도로만 쓰는 편이 안전하다.

참고 자료#

글 공유하기

이 글이 도움이 되셨다면 다른 사람과 공유해 주세요!

UE4 ACharacter::PlayAnimMontage 반환값이 실제 재생시간과 다른 이유
https://velog.io/@hon454/ACharacterPlayAnimMontage의-반환값에-대하여
작성자
Jihoon Jeon
게시일
2021-05-18
라이선스
CC BY-NC-SA 4.0
관련 게시글스마트
1
UE4 BlueprintCallable 함수에 실행 핀이 보이지 않는 이유
Unreal EngineUE 4.27에서 const BlueprintCallable 함수가 자동으로 Pure 노드가 되는 조건과 BlueprintPure=false로 실행 핀을 복원하는 방법을 설명합니다.
2
면접 오답노트: 말하지 못한 주요 개념, Net Relevancy
Unreal Engine면접에서 제대로 답하지 못한 Net Relevancy를 다시 정리한다. AActor::IsNetRelevantFor의 판정 순서와 문서와 다른 부분, Owner가 판정을 바꾸는 방식, 연관성을 잃고 되찾을 때 RelevantTimeout과 액터 파괴, BeginPlay, OnRep, RPC에 생기는 일, ForceNetRelevant와 Replication Graph, Iris에서의 주의점, Dormancy, Priority와의 차이를 다룬다.
3
다수 좀비의 서버 비용과 전송량 줄이기
Unreal EngineNight of the Dead에서 웨이브의 좀비 수백 마리를 Dedicated Server와 클라이언트가 감당하도록, 거리 순위 기반 LOD로 이동 시뮬레이션과 전송률을 조절하고 애니메이션, 길찾기, 상태 복제 비용을 줄인 방법을 정리한다.
4
휴면 액터 깨우기: FlushNetDormancy, ForceNetUpdate, SetNetDormancy
Unreal EngineUE 5.8.3 엔진 소스 기준으로 FlushNetDormancy, ForceNetUpdate, SetNetDormancy가 각각 무엇을 바꾸는지 비교한다. 휴면 정책과 휴면 상태의 차이, ForceNetUpdate가 덤으로 하는 flush가 빠지는 조건, 값을 바꾼 뒤 깨우면 FastArray 변경이 사라지는 문제, 이를 막는 setter 패턴과 Push Model을 함께 쓸 때의 주의점을 정리한다.
5
Unreal Engine PIE에서 클라이언트와 서버 값을 한 줄로 비교하기
Unreal EnginePIE에서 클라이언트 코드가 같은 프로세스의 서버 월드와 서버 측 PlayerController를 찾아 위치·속도를 한 줄로 비교하는 에디터 전용 디버그 코드와, 네트워크 에뮬레이션으로 확인한 측정 결과를 정리합니다.
무작위 게시글무작위

댓글

Profile Image of the Author
Jihoon Jeon
안녕하세요! 서브컬처와 자전거를 사랑하는 개발자 전지훈입니다.
공지사항
제 블로그에 오신 것을 환영합니다! 예시 공지입니다.
카테고리
태그
최신 일상
사이트 통계
게시글
73
카테고리
10
태그
143
총 단어 수
133,527
운영 일수
0일
최근 활동
0일 전
사이트 정보
빌드 플랫폼
GitHub Actions
블로그 버전
Firefly v6.16.9
라이선스
CC BY-NC-SA 4.0
목차