Motion Matching을 사용한 애니메이션 작업
언리얼 모션 매칭으로 이동, 점프, 구르기 동작
개요
- 모션 매칭을 사용하여 2D 평면 이동 및 점프 애니메이션을 재생한다.
- Pose Search Database, Pose Search Schema, Chooser의 기본 사용법을 익힌다.
- 몽타주를 사용하여 구르기 애니메이션을 재생한다.
1) 모션 매칭
모션 매칭이란?
기존의 애니메이션 시스템은 상태 전환을 위하여 스테이트 머신을, 포즈 전환을 위하여 애님 그래프를 사용했다. 미리 애니메이션을 지정해두고 조건을 만족하면 이를 재생하는 방식이라 할 수 있다.
모션 매칭은 이와 다르게 지정된 데이터베이스에서 가장 적합한 애니메이션들을 찾아내어 블랜딩한 결과물을 재생하는 방식이다.
모션 매칭에 사용되는 에셋들
흐름을 정말 대강 나타냈다. 자세한 구조는 공식 문서와 언리얼 페스트의 영상, GameAnimationSample을 참고하자.
-
Pose Search Schema(PSS)
포즈 서치 스키마는 애니메이션 선택 조건을 설정하는 에셋이다. 특정 본의 위치, 속도, 궤적 등을 채널로 구분하고 가중치를 부여한다. 해당 조건들을 기준으로 비용을 계산하여 다음 애니메이션을 선택한다.
-
Pose Search DataBase(PSD)
스키마를 사용해 탐색할 애니메이션들의 집합이다.
-
Chooser
어떠한 조건을 만족했을 때, 어떠한 결과를 출력할 것인지 정하는 에셋이다. 모션매칭과 별개로 사용할 수 있다. 모션 매칭에서는 ABP의 여러 정보를 기반으로 PSD를 선택할 때 사용한다.
-
Animation Blueprint
사용할 모션 매칭 데이터베이스를 정하는 Motion Matching노드, 캐릭터 궤적 정보를 담당하는 Pose History 노드를 사용하여 최종 포즈를 출력한다.
2) 모션 매칭 작업 과정 - 기본 움직임
전반적인 작업 과정은 해당 블로그를 참고했다.
플러그인 설치
- Motioni Trajectory
- Pose Chooser
- Chooser
애니메이션 준비
기본적인 이동 동작들은 AnimationSample에 포함되어있다. 이를 리타겟팅하여 사용하자. 리타겟팅 방법은 블로그의 리타겟팅 관련 포스트에 정리해두었다.
외부 애니메이션을 임포트, 리타겟팅하여 사용할 때는 루트모션에 주의하자. 루트모션을 사용하되, 이상한 위치로 튀지 않는지 확인할 것. 나는 Mixamo의 구르기를 사용하려다가 실패하고 다른 애니메이션을 사용해야 했다.
캐릭터에 TrajectoryComponent 추가
캐릭터 블루프린트에서 추가하면 편리하다. 코드에서 추가할거면 CharacterTrajectoryComponent.h를 삽입하자. MotionTrajectory 플러그인에 있는 헤더파일이므로 Build.cs에 추가하여 사용할 것.
// 캐릭터
#include "CharacterTrajectoryComponent.h"
// Build.cs
PublicDependencyModuleNames.AddRange(new string[] { /*이래저래*/, "MotionTrajectory" });
포즈 서치 스키마, 포즈 서치 데이터베이스 추가
본인은 포즈 서치 스키마 1개, 포즈 서치 데이터베이스는 Idle, Jump, Run, Walk의 4개를 사용했다. AnimationSample을 보면 포즈 서치 데이터베이스가 많은 것을 확인할 수 있다.
동일한 포즈 서치 스키마를 사용한다면 단일 DB가 좋을까 다수 DB가 좋을까? 언리얼 페스트 영상에서는 다수 DB를 사용하는 편이 좋다고 한다. 아무래도 모션 매칭이 정확한 동작을 짚어서 재생하는 것이 아니다보니, 단일 DB를 사용하다보면 원하지 않는 동작이나 어색한 장면이 발생할 수 있기 때문이다.
애니메이션 블루프린트 작업
애니메이션 블루프린트에는 모션 매칭을 위한 Motion Matching, Pose History노드가 존재한다. PoseHistory 노드는 캐릭터의 궤적 정보를 요구하므로 앞서 CharacterTrajectoryComponent를 추가한 캐릭터의 데이터를 전달해주자. 이후 애님 그래프에서 각 노드를 연결하고, 완성된 블루프린트를 캐릭터의 애니메이션으로 지정하면 완성.
Run 포즈 서치 데이터베이스만을 사용했기에 달리기 애니메이션만 적용된 모습이다. 방향 전환 시 급하게 몸을 꺾는 애니메이션이 눈에 띈다.
3) 모션 매칭 작업 과정 - Chooser 적용
캐릭터 상태 구분
이제 달리기, 걷기, 점프, 구르기의 캐릭터 동작 상태를 구분하여 다른 포즈 서치 데이터베이스를 사용해보자. 우선 캐릭터의 동작 상태를 나타내기 위한 enum들을 별도의 헤더에 선언했다.
// IVCharacterStateEnums.h
// 캐릭터 이동 상태
UENUM(BlueprintType)
enum class EMovementState : uint8
{
Idle UMETA(DisplayName = "Idle"),
Move UMETA(DisplayName = "Move"),
};
// Gait는 걸음걸이를 의미함
UENUM(BlueprintType)
enum class EGaitState : uint8
{
Walk UMETA(DisplayName = "Walk"),
Run UMETA(DisplayName = "Run")
};
// 캐릭터 점프 상태
UENUM(BlueprintType)
enum class EJumpState : uint8
{
InAir UMETA(DisplayName = "In Air"),
OnGround UMETA(DisplayName = "On Ground")
};
// 특수 움직임 상태
UENUM(BlueprintType)
enum class ESpecialMovementState : uint8
{
None UMETA(DisplayName = "None"),
Rolling UMETA(DisplayName = "Rolling"),
Dodging UMETA(DisplayName = "Dodging")
};
이러한 정보들은 스탯 관리용으로 만든 액터 컴포넌트인 CharacterStatComponent에서 관리한다. 해당 컴포넌트는 캐릭터에 부착해 사용한다. 플레이어가 캐릭터에게 입력을 전달하면, 캐릭터는 입력을 처리하며 컴포넌트에게 상태 전달을 지시한다. 애님 인스턴스는 이러한 데이터를 받아 작업을 진행한다.
Chooser 사용하기
Chooser는 범용 선택 도구이다. 블루프린트 에셋을 생성하기 위한 접근 경로부터 모션 매칭이 아닌 기타 항목에 존재한다.
Chooser의 디테일 창에는 어떤 요소로부터 데이터를 가져와서 어떠한 결과 타입을 반환할 것인지 지정할 수 있도록 되어있다. 이번에는 ABP로부터 캐릭터 상태 정보를 가져와서 포즈 서치 데이터베이스를 결정하고 반환해보자.
ABP에서 Chooser 연결
Motion Matching 노드를 보면 데이터베이스를 지정할 수 있는 핀이 존재한다. 단일 데이터베이스의 경우에는 지정해주면 끝이었지만, 이제는 동적으로 바뀌어야하니 디테일 창에서 해당 값을 ‘동적 값’으로 지정하자.
그리고 동적 업데이트를 위하여 On Update 함수에 바인딩을 진행한다. 이제 Motion Matching 노드의 데이터베이스가 동적으로 변경된다! 캐릭터를 움직여서 제대로 동작하는지 확인하자.
4) 리와인드 디버거로 이상 동작 거르기
애니메이션이 이상하다
이쯤 되면 어째서 AnimationSample의 PSS와 PSD가 많고 자세했는지 깨달을 수 있다. 애니메이션을 전부 몰아넣고 만든 모션 매칭 결과는 뭔가 이상하고 이해할 수 없게 동작했다. 특히 점프 이후 공중에 떠 있을 때 Idle 자세가 나오는 것을 고치고 싶었다.
리와인드 디버거 & 리와인드 디버거 디테일
상단 메뉴 → 툴 → 디버그에는 리와인드 디버거와 리와인드 디버거 디테일이 존재한다. 이들을 켜두고 시뮬레이션을 진행하면, 타임라인을 따라서 어떠한 애니메이션들이 사용되고 있는지 확인할 수 있다. 문제가 되는 애니메이션을 데이터베이스에서 제거하거나 수정하면 된다.
문제가 되는 시점을 확인해보니 M_Neutral_Jump_F_Land_Walk 애니메이션의 착지 후 걷는 부분이 실행중이었다. 해당 애니메이션을 삭제하여 문제를 해결했다.
5) 구르기 몽타주 추가
몽타주 생성 및 추가
몽타주를 생성하고 코드에 추가하는 방법은 몽타주 관련 포스트에 정리해두었다. 몽타주를 재생할 슬롯의 위치는 AnimationSample을 참고하여 Pose History 노드 이전에 배치했다.
구르는 동안에는 선택기 평가(회색 바)가 잠시 멈추며 몽타주만 재생되는 모습이다.
+) 캐릭터의 PlayAnimMontage vs AnimInstance의 Montage_Play
캐릭터 클래스에서 몽타주를 재생할 때, 캐릭터 클래스의 PlayAnimMontage()를 호출하거나 AnimInstance를 가져와서 AnimInstace→Montage_Play()를 호출할 수 있다.
단순히 재생만 필요하다면 PlayAnimMontage()를 사용하고, 애니메이션 블랜딩이나 재생 속도 조절, 몽타주 재생 관련 대리자를 사용하고싶다면 AnimInstance를 가져와서 사용하자.
6) 작업 결과물
https://youtu.be/NDeicYeclGY?si=ABsU9Scyg4uwDwqP