개요

Unreal Engine 5.3의 TPS 템플릿을 참고하여 애니메이션 블루프린트를 만들어본다.

게임 진행에 필요한 기타 동작들은 나중에 추가하고, 우선 이동과 점프 애니메이션을 적용하는 것이 목표이다.

본문에서 애니메이션 블루프린트는 ABP로 칭한다.

사전 준비

캐릭터 설정

Untitled

TPS 템플릿의 BP_ThirdPersonCharacter를 보면, 애니메이션 모드와 애님 클래스가 지정이 된 것을 확인할 수 있다. 애니메이션 모드에서는 블루프린트 / 에셋 / 커스텀 선택이 가능하다. 여기서는 ABP를 사용하기에 블루프린트로 지정하였다.

본인은 AnimInstance를 상속받은 클래스를 별도로 만들고, 이를 사용한 BP를 캐릭터의 애님 클래스로 지정하여 사용하였다.

애니메이션 준비

미리 준비해둔 애니메이션이 캐릭터에 완벽히 적용되면 정말 좋겠지만, 그렇지 않은 경우에는 IK Rig와 IK Retargeter를 사용하여 애니메이션 리타겟팅을 진행해야한다.

AnimInstance 클래스 준비

AnimInstance는 애니메이션을 만들기 위한 일종의 데이터 집합이다. 에디터에서 ABP를 만들 때 스켈레톤과 부모 클래스가 될 AnimInstance를 선택해야한다. AnimInstance 내부에 정의한 멤버들은 ABP에서 노드로 활용할 수 있다.

Untitled

AnimInstance를 상속받은 PlayerAnimInstance를 생성했다. UPROPERTY에서 멤버 접근을 허용하고이를 기반으로 ABP를 만든다면, 위와 같이 멤버들을 확인할 수 있다. 이들이 표시되지 않는 경우에는 상속된 변수 표시에 체크.

// PlayerAnimInstance.h
protected:
	virtual void NativeInitializeAnimation() override;
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

protected: 
	UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowPrivate = "true"))	
	class ADefaultCharacter* Character;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowPrivate = "true"))
	bool IsInAir;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowPrivate = "true"))
	bool IsMoving;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowPrivate = "true"))
	float MovementSpeed;
  • NativeInitializeAnimation → 초기화 담당
  • NativeUpdateAnimation(float DeltaSeconds) → 업데이트 담당
// AnimInstance.h에 정의되어있다!

// the below functions are the native overrides for each phase
// Native initialization override point
ENGINE_API virtual void NativeInitializeAnimation();
// Native update override point. It is usually a good idea to simply gather data in this step and 
// for the bulk of the work to be done in NativeThreadSafeUpdateAnimation.
ENGINE_API virtual void NativeUpdateAnimation(float DeltaSeconds);
  • Character → 캐릭터 무브먼트 컴포넌트에 접근하여 동작 정보를 얻기 위함
  • IsInAir / IsMoving / MovementSpeed → 스테이트 제어용

이벤트 그래프

Untitled

블루프린트가 생성되었을 때, 캐릭터 무브먼트 컴포넌트를 가져오는 과정이다. 캐릭터 무브먼트 컴포넌트는 인간형 캐릭터의 기본 움직임을 지원하는 액터 컴포넌트이다.

// PlayerAnimInstance.cpp
void UPlayerAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();
	Character = Cast<ADefaultCharacter>(TryGetPawnOwner());
}

동일하게 초기화를 담당하는 NativeInitializeAnimation으로 옮겼다. TryGetPawnOwner()는 AnimInstance의 함수다. ABP 생성시 지정한 스켈레톤에 접근하여 그 Owner를 Pawn 타입으로 반환한다. 나는 캐릭터로 캐스팅하여 사용했다.

Untitled

다른 내용들은 ABP에서 사용하는 변수들의 세팅에 관련된 내용들이다. 위에서 캐릭터 컴포넌트를 얻어오고, 존재한다면 XY평면에서의 속도 / 이동 여부 / 공중 부양 여부를 확인한다. 이동 여부에서 최소 속도값이 존재하는 이유는 아주 조그만 입력은 무시하기 위함이다.

// PlayerAnimInstance.cpp
void UPlayerAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);
	if (Character) 
	{
		// Jump
		IsInAir = Character->GetCharacterMovement()->IsFalling();

		// Movement
		IsMoving = Character->GetCharacterMovement()->IsMovingOnGround();

		FVector velocity = Character->GetVelocity();
		MovementSpeed = FVector(velocity.X, velocity.Y, 0.0f).Size();
		
		// Aim
		ControllerYaw = Character->GetControlRotation().Yaw;
		ControllerPitch = Character->GetControlRotation().Pitch;
	}
}

업데이트를 담당하는 NativeUpdateAnimation에서 이들을 처리했다. 움직임과 공중 부양 여부는 캐릭터 무브먼트 컴포넌트에서 제공하는 위의 함수들을 사용하였다. 속도는 루트컴포넌트의 것을 가져와서 X,Y만 별도로 크기를 측정해 사용하였다.

ControllerYaw, ControllerPitch는 이후에 Aim Offset을 사용하기 위해 추가하였다.

애니메이션 그래프

Untitled

TPS 템플릿의 애니메이션 그래프에는 Control Rig를 활용해 IK를 적용하는 과정이 있다. 지금은 ABP 동작 확인이 우선이니 해당 부분은 걷어내었다. 이외의 애니메이션 그래프 구성은 TPS 템플릿의 구성과 거의 흡사하다.

  • 정지-걷기-달리기는 블랜드 스페이스를 사용하여 속도에 따라 변하도록 하였다.
  • Chached Pose를 사용해 Move 동작을 별도로 분리하였다. 해당 동작은 Main State 내부에서 사용.
  • 스테이트 에일리어스를 사용해 공중 동작과 지상 동작을 분리하였다.

참고자료