본문으로 바로가기

[Unreal Engine 5] 캐릭터 애니메이션

category UnrealEngine/기능 2023. 9. 19. 22:25

언리얼 C++에서 애니메이션을 플레이 하는 방법에 대해서 작성합니다.

 

Pawn

Pawn은 Actor를 상속받은 특별한 객체이며, 플레이어가 빙의해 입출력을 처리하도록 설계되어 있습니다.

Pawn은 길찾기를 사용할 수 있으며, 일반적으로 세 가지 주요 컴포넌트로 구성됩니다.

  • 기믹과 상호작용을 담당하는 CollisionComponent
  • 시각적인 비주얼을 담당하는 MeshComponent
  • 움직임을 담당하는 MoveComponent

이 중에서, Transform이 없이 기능만 제공하는 것을 ActorComponent 라고합니다.

 

Character의 기본 구조

캐릭터는 인간형 Pawn을 구성하도록 언리얼이 제공하는 전문 Pawn Class 를 의미합니다.

캐릭터는 세 가지 주요 컴포넌트로 구성되어 있습니다.

  • 기믹과 상호작용을 담당하는 CapsuleComponent
  • 애니메이션 캐릭터를 표현하는 SkeletalMeshComponent
  • 캐릭터의 움직임을 담당하는 CharacterMovementComponent

그 중 애니메이션을 위해선 SkeletalMeshComponent가 필요합니다.

SkeletalMeshComponentSkeletalMesh 의 인스턴스를 만들고 조작하는데 사용합니다.

이 인스턴스는 USkeletalMesh인데, 이 안에는 복잡한 Skeleton (우리가 3D 모델링과 리깅을 통해서 만들어진 수 많은 본들과 버텍스들의 뭉치 )이 있고

이 메시와 버텍스들을 현재 재생중인 애니메이션에 일치하도록 돕는것도 Component의 역할입니다.

 

하지만 애니메이션을 어떻게 제어할지는 '프로그래머'의 몫이며 그 제어 방법을

Animation Blueprint 또는 Animation Sequence를 통해 제공해야 합니다.

이 문서에서는 Animation Blueprint SkeletalMeshComponent를 통해 캐릭터를 애니메이션 하는 방법을 설명합니다.

 

1. 모델 구하기

일단 우리가 애니메이션을 위해선 모델 파일과 애니메이션 파일이 필요합니다.

이 문서에서는 무료 에셋인 Infinity Blade와 Mixamo를 사용합니다.

이미 이 문서가 아닌, 다른 문서에서도 모델을 구하는 방법에 대해 많이 다루고 있으므로 이 문서에서 다루진 않겠습니다.

 

1-1. AnimInstance와 AnimGraph

우리가 Animation Blueprint를 통해 애니메이션 시스템을 제작하기 전에, 두 가지 구성 요소를 알아야 합니다.

  • AnimInstance : 애니메이션을 만들기 위해 필요한 데이터나 기능들을 모아둔 객체
                              SkeletalMesh를 소유하는 Pawn(Character)의 정보를 받아 AnimGraph 가 참조할 데이터를 제공함
  • AnimGraph : AnimInstance에서 제공해준 변수 값에 따라 변화하는 애니메이션을 설계하는 공간 

우리가 애니메이션을 제어할 때, AnimGraph에서 어떠한 정보가 변화하였는지에 따라서 StateMachine형태로 변경합니다.

이 때, AnimGraph에 정보를 넘겨주기 위해 AnimInstance가 필요합니다.

그래서 일단, AnimInstance를 생성합니다.

자신이 원하는 네이밍을 하여 AnimInstance를 생성했다면, 다음으로 넘어갑니다.

 

 

2. Animation Blueprint ( AnimGraph ) 만들기

모델을 구했다면, 이 모델을 USkeletalMesh를 통해 불러와 실제 게임 내에서 보여줄 수 있습니다.

 

하지만 우리는 실제로 애니메이션 작업을 해야 합니다.

위에서 설명했듯이, 우리는 애니메이션이 어떻게 제어되어야 하는지를 언리얼 엔진에게 알려주어야 합니다.

이를 Animation Blueprint로 처리할 것이므로, 일단 Animation Blueprint를 생성합니다.

원하는 폴더에서 우클릭 후 Animation - Animation Blueprint

 

우리가 애니메이션에 사용할 Skeleton과 AnimInstance를 선택한 후 Create 합니다.

 

이제 만들어진 Animation Blueprint를 누른 후 위의 Class Settings를 눌러봅시다.

이렇게 Class Settings를 누르면, Detail 창에서 Parent Class로 AnimInstance를 선택할 수 있습니다.

우리가 1-1에서 만들었던 AnimInstance를 등록해줍니다.

 

이제 우리는 Animation Blueprint를 통해서 애니메이션을 제어할 수 있게 되었습니다.

(사실 Animation Blueprint는 AnimInstance와 AnimGraph를 묶어서 처리하는 하나의 블루프린트라고 생각하는게 더 쉬움)

 

3. 캐릭터 애니메이션 시스템의 설계

Animation Blueprint는 Event Graph와 Anim Graph 두 영역으로 구성되어 있습니다.

  • Event Graph : 이벤트로부터 상태를 파악할 수 있는 주요 변수를 저장하는데 사용
  • Anim Graph : Anim Instance에 저장된 변수로부터 지정된 상태의 애니메이션을 재생

이벤트 그래프에서 어떠한 특정 이벤트가 일어났다면, AnimInstance에서 데이터를 처리하고 저장합니다.

 

Event Graph가 이해가 안되실수도 있지만, 간단하게 생각하면 쉽습니다.

Event Graph를 통해 발생한 이벤트를 AnimInstance가 캐치(콜백)하고, 데이터를 처리하여 저장하면 AnimGraph가 사용하는 방식입니다.

그럼 EventGraph를 통해 AnimInstance에서 데이터를 받고, 그것을 처리해 보겠습니다.

 

4. 데이터 처리

각 각의 변수 사용처는 다음과 같습니다.

 

Owner : 현재 UABAnimInstance를 가지고 있는 캐릭터의 정보입니다.

Movement : Owner 캐릭터의 MovementComponent 입니다.

Velocity : 캐릭터가 움직이는 속도 Vector입니다.

GroundSpeed : Velocity를 float으로 변환한 속도 값입니다.

bIsIdle : 캐릭터가 현재 가만히 서있는지 (어떠한 조작도 없는지)를 나타냅니다.

MovingThreshould : 특정 속도를 기준으로 캐릭터가 가만히 있는지, 움직이는지를 나타낼 것인데, 그 기준입니다.

bIsFalling : 캐릭터가 현재 떨어지고 있는지 (바닥에서 떨어졌거나, 점프 후 최고점에서 이제 내려오는 중 등 ) 를 판단합니다.

bIsJumping : 캐릭터가 점프를 했는지 나타냅니다.

JumpingThreshould : 특정 위치를 기준으로 캐릭터가 점프를 했는지 안했는지를 나타냅니다.

 

이제 .cpp 에서 다음과 같이 작성합니다.

 

이제 AnimGraph를 위한 데이터 저장은 다 끝났습니다.

컴파일을 한 후, AnimationBlueprint로 돌아가 My Blueprint에서 

'Show Inherited Variables' 를 체크 해주면, 우리가 저장해둔 변수 값들이 보이게 됩니다.

 

이제 AnimGraph에서 애니메이션이 어떻게 플레이 할지 작성하면, 캐릭터의 SkeletalMesh는 애니메이션을 할 수 있습니다.

 

5. AnimGraph 처리

이제 AnimationBlueprint에서 AnimGraph를 키고 우클릭을 하여 State Machine 노드를 생성합니다.

 

노드를 선택한 후, F2를 누르면 이름을 변경할 수 있습니다.

 

우리가 StateMachine 내에서 애니메이션을 제어하고, 그 결과가 Output에 반영되어야 합니다.

그러므로 MainStateMachine 의 노드를 끌어당겨 Output Pose의 Result에 연결해줍니다.

 

일단 간단한 걷기, 뛰기 모션만 제작하겠습니다.

MainStateMachine을 더블클릭해 MainStateMachine 내부의 노드에서 작업을 시작합니다.

 

우클릭을 하면 Add State를 찾을 수 있습니다.

이를 클릭하여 새로운 State를 생성합니다.

 

우리는 서있기, 걷기, 점프를 만들것이므로 해당하는 State들을 생성해줍니다.

해당 문서에서는 State Alias를 사용할 것이므로 Locomotion, Land, ToLand만 생성했습니다.

 

생각해보면, 애니메이션이 많아지면 그 만큼 노드들도 많아지고 잇게되는 줄들이 많아져 관리가 어렵습니다.

그래서 하나의 상태씩을 묶어 미리 저장해두고, 이를 가져다 쓰기만 하는 방식으로 분리하여 관리할 수 있습니다.

이를 위해 일단 차상위인 AnimGraph로 다시 돌아갑니다.

그리고 StateMachine을 하나 만들어주겠습니다.

 

 

만들어진 StateMachine에 들어가서, Idle 상태와 움직이는 상태에 대한 State를 만들어주고 서로 잇습니다.

 

여기서 잇는것은 하나의 State에서 드래그 하면 줄이 나옵니다. 그 줄을 '전이' 할 다른 State로 드래그 하면 됩니다.

이 '전이' 란 어떠한 '상태' 를 만족하면 해당 state로 넘어간다는 뜻입니다.

예를 들어, 우리는 가만히 있는 Idle 상태에서 걸을때는 walk 상태로 넘어가야 합니다. 

그럴땐 Idle 노드를 드래그 하여 Walk 노드에 연결 한 후, 조건을 '캐릭터가 움직이는가?' 로 하면 되는 것입니다.

 

이제 이 조건을 정하기 위해서 이어진 노드들 사이에 있는 Transition 버튼을 클릭 해봅니다.

 

움직이지 않을 경우에 Walk로, 움직일 경우엔 Idle로 변경되어야 하므로 상태를 정하고 저장합니다.

 

그리고 Idle, IdleWalkRun 노드를 더블클릭 해보면, 각각의 상태가 어떤 애니메이션을 플레이 해야 하는지를 정할 수 있습니다.

자신이 사용하길 원하는 애니메이션을 Asset Browser에서 끌고와 놓고, Output Animation Pose에 연결해주면 됩니다.

 

이렇게 우리가 Idle, Walk의 애니메이션을 제어완료했다면, 이 애니메이션의 결과를 어딘가에 저장해두어야 합니다.

이를 위해 저장시키는 노드를 하나 만들어 결과물을 저장합니다.

그리고 노드를 잇게 되면, 해당 state에서 나온 결과물을 항상 저장해둡니다.

 

이제 다시 MainStateMachine으로 돌아가, 우리가 저장해두는 결과물을 사용해야 합니다.

해당 state를 사용할 노드에 들어가서 노드를 만들고 연결합니다.

이러면 항상 Locomotion State에서는 저장된 'LocomotionCache'의 결과물을 사용합니다.

 

이제 특정 상태일때, 전이가 가능한 State Alias를 추가하겠습니다.

우리가 Locomotion 상태일 때, 점프가 가능해야 합니다.

그러므로 ToJump라는 State Alias를 만들어보겠습니다.

눈치 채셨겠지만, 오른쪽의 state alias에서 우리가 만들었던 state들의 목록이 나옵니다.

즉 '해당 state에서 특정 조건을 만족하면 이 노드에 들어온다' 라고 생각하시면 됩니다.

우리는 Locomotion에서 점프가 가능하도록 할 것이므로 Locomotion에만 체크합니다.

 

우리가 만든 state alias에서 특정 조건은 2가지가 있습니다.

'점프 중 이거나', '떨어지거나' 

우리의 애니메이션은 계속 Locomotion을 플레이 하나 위의 조건이 만족한다면

해당 애니메이션을 실행합니다. 그러므로 Locomotion은 묻히게 되는 것입니다.

 

이제 Transition에서 우리가 미리 저장해둔 bIsJumping, bIsFalling을 사용하여 Transition을 추가합니다.

그리고 Jump를 했을 때 최고점에서 점점 중력에 의해 떨어질때는 Falling 상태가 되어야 하므로 Jump에서 Falling으로 Transition도 추가 해줍니다.

여기서 TimeRemaininig은, Transition 하는 과정에서 이전의 애니메이션의 플레이 시간이 얼마나 남았는가 입니다.

Jump 애니메이션의 플레이 타임 비율이 0.1 이하가 되면 Falling 애니메이션이 플레이 되도록 합니다.

 

이로서 애니메이션이 끝이 났습니다.

만약 착지 모션도 만들고 싶다고 하면 다음과 같이 만들 수 있습니다.

 

Falling상태일때, bIsFalling이 False 라면 그것은 이제 막 착지를 했다는 뜻이므로 ToLand에서 Locomotion 까지의 Transition을 만들 수 있습니다.