KoreanFoodie's Study

이득우의 언리얼 C++ 7 : 애니메이션 시스템의 설계 본문

Game Dev/Unreal C++ : Tutorial

이득우의 언리얼 C++ 7 : 애니메이션 시스템의 설계

GoldGiver 2022. 3. 1. 10:38

이득우님의 "이득우의 언리얼 C++ 게임 개발의 정석" 책을 따라가며 실습한 내용을 정리한 포스팅입니다. 실습에 필요한 자료들은 이 링크에서, 제가 작업한 예제 소스 완성본은 여기에서 찾아보실 수 있습니다. (저는 언리얼 4.27.2 버전 기준으로 작업하였습니다)

애니메이션 블루프린트 : 

애니메이션 블루프린트를 이용해 애니메이션 시스템을 제작하기 전에, 먼저 두 가지 구성 요소를 알아보자.

  • 애님 인스턴스 : 스켈레탈 메시를 소유하는 폰의 정보를 받아 애님 그래프가 참조할 데이터를 제공한다. 블루프린트와 C++ 로 제작할 수 있다.
  • 애님 그래프 : 애님 인스턴스의 변수 값에 따라 변화하는 애니메이션 시스템을 설계하는 공간이다. 블루프린트로만 제작할 수 있다.

 

엔진에서 "새로운 C++ 클래스 (모든 클래스 표시) -> AnimInstance 부모 클래스 선택" 후 ABAnimInstance 를 생성했다.

해당 AnimInstance 에서는 Pawn 의 속도를 읽기 위한 CurrentPawnSpeed 와 점프를 했을 때 공중에 떠 있는지를 체크하기 위한 IsInAir 를 추가한다.

 

AnimInstance.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "ArenaBattle.h"
#include "Animation/AnimInstance.h"
#include "ABAnimInstance.generated.h"

/**
 * 
 */
UCLASS()
class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
	
public:
	UABAnimInstance();
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

private:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Pawn, Meta=(AllowPrivateAccess=true))
	float CurrentPawnSpeed;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Pawn, Meta=(AllowPrivateAccess=true))
	bool IsInAir;
};

UPROPERTY 설정을 보면, 블루프린트에서는 읽을수만 있도록 설정했다.

 

ABAnimInstance.cpp

UABAnimInstance::UABAnimInstance()
{
	CurrentPawnSpeed = 0.0f;
}

초기값은 0으로 설정한다.

 

 

이전에 우리가 만든 애니메이션 블루프린트에서 애님 그래프로 들어가 보자.

먼저, 클래스 세팅에서 부모 클래스를 방금 생성한 ABAnimInstance 로 설정해 준다.

 

그 후, 상속된 변수를 표시하여 CurrentPawnSpeed 를 볼 수 있도록 만든다. CurrentPawnSpeed 가 0 보다 클 때, "bool 로 포즈 블렌딩" 을 사용하여 각각 WarriorRun 과 WarriorIdle 애니메이션을 재생한다.

 

 

폰과 데이터 연동

위에서 설명을 건너뛴 함수가 있다.

UABAnimInstance();
virtual void NativeUpdateAnimation(float DeltaSeconds) override;

위의 NativeUpdateAnimation 은 애님 인스턴스 클래스에서 틱마다 호출되는 가상 함수이다. 이 함수를 이용해서, 애님 인스턴스에서 폰의 정보를 얻어올 것이다. 사실 폰의 Tick 함수에서 애님 인스턴스의 CurrentPawnSpeed 를 조작할 수도 있지만, 흐름상 "입력 시스템 -> 게임 로직 -> 애니메이션 재생" 이 자연스럽다.

또한 애님 인스턴스에서 폰의 정보를 조회하는 방식을 사용하면 서버에서는 불필요한 애니메이션 정보 없이 로직 정보만 저장할 수도 있다.

 

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

	auto Pawn = TryGetPawnOwner();
	if (::IsValid(Pawn))
	{
		CurrentPawnSpeed = Pawn->GetVelocity().Size();
		auto Character = Cast<ACharacter>(Pawn);
		if (Character)
		{
			IsInAir = Character->GetMovementComponent()->IsFalling();
		}
	}
}

TryGetPawnOwner( ) 함수를 이용해 폰 객체가 유효한지를 점검한 후, 속도 값을 받아 온다.

 

또한 폰을 캐릭터 액터로 받아 IsInAir 상태를 체크한다. 이때 GetMovementComponent 에서 이를 체크하는데, 폰의 무브먼트 컴포넌트가 제공하는 함수들은 다음과 같다 :

  • IsFalling( ) : 공중에 떠 있는지 알려줌
  • IsSwimming( ) : 수영 중인지 알려줌
  • IsCrouching( ) : (쭈그리고) 앉아있는지 알려줌
  • IsMoveOnGround( ) : 땅 위에서 이동 중인지 알려줌

폰 무브먼트 모델에는 위의 함수가 설계되어 있지만, FloatingPawnMovement 컴포넌트는 해당 함수에 대해 모두 false 값을 반환한다. 따라서 실제로 이 기능을 제대로 구현한 컴포넌트는 캐릭터 무브먼트 컴포넌트 뿐이다.

 

 

스테이트 머신의 제작

C++ 클래스에서 값을 불러오고 업데이트할 준비가 완료되었으니, 본격적으로 AnimGraph 를 건드려 Transition 을 변경시켜 보자. 먼저 Jump 기능을 구현할 것이다. Jump 는 프로젝트 세팅에서 스페이스 바를 사용하도록 이미 매핑이 되어 있다.

 

ABCharacter.cpp

AABCharacter::AABCharacter()
{
    GetCharacterMovement()->JumpZVelocity = 800.0f;
}

void AABCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent:IE_Pressed, this, &ACharacter::Jump);
}

점프 높이를 800 으로 설정하고, Jump 를 바인딩해 준다.

 

우리는 다음과 같이 스테이트 머신을 구성할 것이다.

전체 AnimGraph
BaseAction
GroudAction
Ground -> JumpStart (Transition), JumpEnd -> Ground 는 Not 만 붙여주면 된다.
JumpStart (에셋을 연결시켜준다. 다른 항목도 동일하게 진행)&nbsp;
JumpStart -> JumpLoop (Transition) : 재생 시간이 10% (0.1) 이하로 남으면 전환. JumpLoop -> JumpEnd 도 동일하다.
이때, Loop Animation 은 체크 해제 한다.
점프가 잘 되는지 체크해 보자

 

 

애니메이션 리타깃

위에서 설정한 점프 애니메이션은 사실 InfinityBladeWarriors 가 아닌, 기본 제공 애니메이션에서 가져온 에셋이다. 언리얼은 인간형 캐릭터의 경우 스켈레톤의 구성이 달라도 애니메이션을 교환할 수 있는 기능(리타깃)을 제공한다.

먼저, 리타깃할 마네킹을 열고, "릭 선택" 에서 인간형 릭을 선택한다.

 

매핑을 해주는 작업을 Warrior 스켈레탈에도 똑같이 진행한다.

 

복사할 애니메이션에서 "애님 에셋 리타깃 -> 애님 에셋 복제후 리타깃" 을 선택한다.

 

리타깃할 Warrior 스켈레톤을 선택하고, 편의를 위해 접두사를 바꾼후 리타깃을 누르면 된다. 이제 Warrior 스켈레톤도 기본 Mannequin 의 애니메이션을 사용할 수 있게 되었다.

 

 

참고 : Automatic Rule Based on Sequence Player in State

해당 옵션을 체크하면, State 에서 애니메이션이 끝나면 자동으로 State 가 Transition 된다.

 
Comments