[UE5] 애니메이션 적용

2024. 2. 15. 09:55강의/UE5 기타 강의들

3D 게임에서의 애니메이션은 2d 게임과는 다르게 애니메이션간의 보간이 필수입니다.

그 이유는, 동작에서 동작으로 넘어갈 때 자연스러워야하기 때문입니다.

 

언리얼에서의 애니메이션의 주요 요소는 다음과 같습니다.

 - **AnimInstance**: 애니메이션 데이터 관리 및 그래프 작성과 같은 애니메이션 재생 제어

 - **BlendSpace**: 애니메이션간 보간 (Idle -> Walk 등을 설정, 특정 프로퍼티 조합으로 혼합)

 

 

이 글에서는 BlendSpace는 다루지 않습니다.

- 공식문서에서 적용하는 방법을 이미지로 쉽게 알려주기 때문입니다.

- 하지만, AnimInstance는 BP로만 다루고 간단히만 다루고 있어서 따로 정리하기위한 글입니다.
  (Alias, Additive, Cached Pose를 추가적으로 작성했습니다.)

- [https://docs.unrealengine.com/5.3/ko/setting-up-character-movement/](https://docs.unrealengine.com/5.3/ko/setting-up-character-movement/)

 

1. AnimInstance

 

- AnimInstance는 애니메이션을 만들기 위해 필요한 데이터나 기능들을 모아둔 객체 인스턴스입니다.

- 애니메이션 BP는 이 AnimInstance를 상속합니다.

- Skeletal Mesh에 애니메이션 BP를 지정하면, 캐릭터가 초기화 될 때 AnimInstance 클래스의 인스턴스가 생성되며, 애니메이션 시스템이 활성화됩니다.

 

- 또한, C++로 AnimInstance를 커스텀한 클래스를 사용하면, 애니메이션을 더 많이 제어할 수 있습니다.

- 이 때, 주로 override하는 메서드는 두 가지입니다.

virtual void NativeInitializeAnimation() override;
virtual void NativeUpdateAnimation(float DeltaTimeX) override;

 

 

1.1 NativeInitializeAnimation

void UStwAnimInstance::NativeInitializeAnimation()
{
    Super::NativeInitializeAnimation();

    Owner = Cast<ACharacter>(GetOwningActor());
    if (Owner)
    {
        Movement = Owner->GetCharacterMovement();
    }
}

 

  • BeginPlay와 유사합니다.
  • 애니메이션에서 필요한 데이터를 초기화합니다.
  • 주로 Pawn 즉, Owner, Movement 정보를 설정합니다.

 

1.2 NativeUpdateAnimation

void UStwAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
    Super::NativeUpdateAnimation(DeltaSeconds);

    if (Movement)
    {
        const FVector Velocity = Movement->Velocity;

        Speed = Velocity.Length();
        Direction = UKismetAnimationLibrary::CalculateDirection(Velocity, Owner->GetActorRotation());

        bIsFalling = Movement->IsFalling();
        bIsCrouched =  Owner->bIsCrouched;
        bIsJumping = Owner->bPressedJump;
       //    bIsJumping = bIsFalling & (Velocity.Z > JumpingThreshould);
    }
}
  • Tick과 유사합니다.
  • 애니메이션에 필요한 데이터들을 업데이트해주는 로직을 작성합니다. (Movement 데이터에 따라 bool 플래그 설정)
  • 위 코드는 공식문서에 나와있는 bp와 같습니다. (물론, 헤더파일에도 저 변수들을 선언해주어야 합니다.)

 

1.3 AnimInstance 구조

- 크게 이벤트 그래프애님 그래프 두 가지로 구성됩니다.

    - 이벤트 그래프는 발생된 이벤트로부터 상태를 파악하는 변수값을 저장하는데 사용됩니다. (위 두 함수가 이에 해당합니다.)

    - 이벤트 그래프에서 얻은 데이터를 애님 그래프에서 사용합니다.

    - 애님그래프는 스테이트 머신으로 얻은 데이터에 따라 상태를 변경시키며, 적절한 애니메이션을 재생시킵니다. 

 

 

2. 애니메이션 스테이트 머신

  • 스테이트 머신이란? 
    • 스켈레탈 메시 애니메이션을 State로 구성하는 것 입니다.
    • State를 Transition을 통해 제어합니다.
      • Transition Rules: 하나의 State를 다른 State와 블렌딩하는 방식을 제어

 

2.1 애님 그래프 Movement State 

 

- 애님그래프: 스켈레탈 메시의 최종 포즈를 평가하는데 사용합니다.

 

 

- 애님 그래프 > 우클릭 > State Machine 추가해줍니다.

 

 

- Locomotion** 이라고 이름 변경 후, Add State 를 통해 State를 추가합니다.

 

- State는 Movement로 이름짓고, BlendSpace 를 끌어다 넣어서 애니메이션을 추가해줍니다.

 

 

- 위와 같이, 변수들을 연결해주고 컴파일 및 저장을 해줍니다.

 

 

 

- 그후, OutputPose에 연결시킨 다음 변수들을 수정하여 BlendSpace에서 Blend한 애니메이션을 볼 수 있습니다.

 

// APlayerCharacter.cpp 생성자

	GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint);

	static ConstructorHelpers::FObjectFinder<USkeletalMesh> CharacterMeshRef(TEXT("/Script/Engine.SkeletalMesh'/Game/AnimStarterPack/UE4_Mannequin/Mesh/SK_Mannequin.SK_Mannequin'"));
	if (CharacterMeshRef.Object)
	{
		GetMesh()->SetSkeletalMesh(CharacterMeshRef.Object);
	}

	static ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstanceClassRef(TEXT("/Script/Engine.AnimBlueprint'/Game/Un/Animation/Character/ABP_PlayerCharacter.ABP_PlayerCharacter_C'"));
	if (AnimInstanceClassRef.Class)
	{
		GetMesh()->SetAnimInstanceClass(AnimInstanceClassRef.Class);
	}

 

 

- 위 코드를 작성하면 아래와 같은 결과를 얻을 수 있습니다.

 

2.2 Crouch State (Transition Rule)

 

- 다음은 웅크리기입니다.

- 위 Transition Rule 노드를 더블클릭하여 애님그래프를 위와 같이 수정해줍시다.

 

 

 

- 그리고, Crouch 노드를 위와 같이 설정해줍니다.

 

 

- Crouch에서 Movement로 가는 Transition Rule 또한 추가해야합니다.

 

2.3 Jump (Start, Loop, Land)

출처:&nbsp; https://docs.unrealengine.com/5.3/ko/state-machines-in-unreal-engine/

 

- 점프는 위 두 애니메이션과는 다르게, 어느 지형에서 점프했는지에 따라 애니메이션의 길이가 변합니다.

- 따라서, Start, Loop, Land 세가지로 나누어서 처리합니다.

- 여기서 bIsJumping으로 Transition 조건을 걸어줍니다.

    - 이 bIsJumping은 IsFalling 과 z축 속도가 일정량 이상일 경우를 의미합니다.

     (점프 시작시, 초기 속도는 양수이며, 점프 최대 높이에 도달하면 velocity는 0이되며,

     착지전까지 velocity는 음수 방향으로 커지게됩니다.)

     ( Jump 함수를 호출하면, JumpZVelocity 멤버 변수에 지정된 값만큼의 점프 속력을 가지고 점프)

 

Priority Order 수정 또한 필요합니다!

 

- 또한 도약, 체공, 착지 애니메이션은 상황에 맞게 애니메이션 재생 시간 조절하는 것이 좋습니다.

    - 도약 착지는 loop를 꺼야합니다.

    - 이 경우, Automatic Rule Based on Sequence Player in State 옵션을 사용하여 애니메이션 종료시 자동으로 상태전환 가능하게 할 수 있습니다.

3. Pose Cache, Apply Additive

 

- 위처럼 구현시 착지시 땅에 꺼지는 문제점이 발생할 수 도 있습니다.

 

 

- 저같은 경우, Unreal5의 기본 마네킹의 Land 애니메이션이 Additive 로 세팅되어 있기 때문에 발생했습니다.

 

- Additive 세팅된 애니메이션은 어떤 애니메이션(base)에 더해주는 것으로  

- "Apply Additive" 노드를 사용해주어야 원하는 결과를 얻을 수 있습니다.

 

출처:&nbsp;https://www.youtube.com/watch?v=flHL3qJB3_I

 

- 위 이미지처럼 걷는 애니메이션 + 앞쪽으로 쏠린 포즈를 합쳐서 다양한 애니메이션을 만드는데 사용합니다.  

- 이를 통해 더 자연스러운 애니메이션을 구현할 수 있습니다. (걷기 + 공격, Layerd Animation보다 자연스러울 가능성이 있음)

- 하지만, 엉뚱한 애니메이션에 Additive를 적용하면, 원하는 결과가 안나올 가능성이 높습니다.

 

- Additive에 사용하는 Base Pose는 주로 여러군데서 사용하는 Pose가 됩니다.

- 여기서는, 착지시 자연스럽게 더해주어야하는 애니메이션은 Idle... Locomotion(Idle <-> Move)이 됩니다.

- 즉, 두 곳에서 사용된다는거죠.

- 이렇게 여러군데서 애니메이션을 사용할 때 사용하는 것이 바로 Cached Pose입니다.

 

- 위와 같이 Cached Pose를 만들고, State를 연결해주면 끝입니다.

 

 

 

- 그리고 이 State에서는 Additive base에 들어갈 애니메이션을 설정하면됩니다.

 

 

- Land는 위와같이 "Use Cached Pose"를 Base에 넣고, Apply Additive를 사용합니다.

 

 

- 이제 캐릭터가 사라지는 문제가 사라진 것을 확인할 수 있습니다.

- 또한, 착지시 점프한 최대 높이에 따라 Alpha값을 조절하게된다면, 더 자연스러운 애니메이션으로 구현할 수 있을거라 생각합니다.

 

4. State Alias

출처:&nbsp; https://docs.unrealengine.com/5.3/ko/state-machines-in-unreal-engine/

 

- 위처럼 Locomotion과 Land를 FallLoop 라는 별명을 붙여 관리할 수 있습니다. 

- 이를 통해 그래프를 간소화시킬 수 있습니다.

출처:&nbsp; https://docs.unrealengine.com/5.3/ko/state-machines-in-unreal-engine/

 

사용방법은 다음과 같습니다.

- 우클릭 > Alias 생성 

- Alias 클릭 후, Detail 창에서 State 체크 (위에선 Locomotion과 Land)

 

 

 

5. 주의사항

  • 게임 엔진은 틱마다 입력 시스템 > 게임 로직 > 애니메이션 시스템순으로 로직을 실행함
  • 플레이어의 입력을 먼저 받고 해석 후 폰을 움직이게 만들고, 그에 맞는 애니메이션을 재생시키는 방식
  • 만일 애니메이션 앞 단계인 게임 로직에서 폰을 제거하면, 그 뒤에 실행되는 애니메이션 로직은 유효하지 않은 폰 객체를 참조하게됨
  • 그러므로 애니메이션 시스템에서 폰에 접근할 때, 폰 객체 유효 검사를 수행해야함
  • TryGetPawnOwner 가 이를 수행하는 함수

 

> 모션들을 Apply Additive라는 노드를 통해서 혼합

 

 

6. 후기

 

 

 

- 아직 Foot IK 를 적용하지 않아서 어색한 부분이 있어서 빨리 추가해야겠다고 생각했습니다.

- 또한, Lyra의 Camera 에서는 Crouch에 대한 보정이 있었는데, 이 부분을 빨리 반영해야겠다고 생각했습니다.

 

7. 참고

- 인프런 이득우의 언리얼 프로그래밍 Part2

- https://docs.unrealengine.com/5.3/ko/setting-up-character-movement/

- https://docs.unrealengine.com/5.3/ko/state-machines-in-unreal-engine/

- https://husk321.tistory.com/418