KoreanFoodie's Study
이득우의 언리얼 C++ 7 : 애니메이션 시스템의 설계 본문
이득우님의 "이득우의 언리얼 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 를 바인딩해 준다.
우리는 다음과 같이 스테이트 머신을 구성할 것이다.
애니메이션 리타깃
위에서 설정한 점프 애니메이션은 사실 InfinityBladeWarriors 가 아닌, 기본 제공 애니메이션에서 가져온 에셋이다. 언리얼은 인간형 캐릭터의 경우 스켈레톤의 구성이 달라도 애니메이션을 교환할 수 있는 기능(리타깃)을 제공한다.
먼저, 리타깃할 마네킹을 열고, "릭 선택" 에서 인간형 릭을 선택한다.
매핑을 해주는 작업을 Warrior 스켈레탈에도 똑같이 진행한다.
복사할 애니메이션에서 "애님 에셋 리타깃 -> 애님 에셋 복제후 리타깃" 을 선택한다.
리타깃할 Warrior 스켈레톤을 선택하고, 편의를 위해 접두사를 바꾼후 리타깃을 누르면 된다. 이제 Warrior 스켈레톤도 기본 Mannequin 의 애니메이션을 사용할 수 있게 되었다.
참고 : Automatic Rule Based on Sequence Player in State
해당 옵션을 체크하면, State 에서 애니메이션이 끝나면 자동으로 State 가 Transition 된다.
'Game Dev > Unreal C++ : Tutorial' 카테고리의 다른 글
이득우의 언리얼 C++ 9 : 콜리전(오브젝트 채널, 트레이스 채널)과 대미지 프레임워크 (2) | 2022.03.06 |
---|---|
이득우의 언리얼 C++ 8 : 애니메이션 시스템 활용 (Montage, Notify, ComboAttack) (0) | 2022.03.04 |
이득우의 언리얼 C++ 6 : 캐릭터의 제작과 컨트롤 (0) | 2022.02.28 |
이득우의 언리얼 C++ 5 : 폰의 제작과 조작 (0) | 2022.02.27 |
이득우의 언리얼 C++ 4 : 게임 모드(GameMode), 플레이어 컨트롤러(PlayerController) 제작 (1) | 2022.02.21 |