2-4강 Character Animation

Ryan Ham·2024년 6월 27일
0

이득우 Unreal

목록 보기
6/23
post-thumbnail

강의 목표

  • Character의 Animation system을 생성하는 방법의 학습
  • Character의 Animation system을 효과적으로 설계하는 방법의 학습

코드 구성

  1. C++로 AnimInstance를 상속하는 클래스를 만든다.
  2. Animation Blueprint를 만들고 skeletal mesh 설정. 위에서 만든 클래스를 부모로 지정한다.

Character Animation 시스템의 생성(아주 중요!!)

  • Skeletal Mesh Component의 Animation Blueprint Class를 지정한다.
  • 캐릭터가 초기화될때 AnimInstance Class의 Instance를 생성한다.
  • 캐릭터는 GetAnimInstance()를 사용해 Animation Instance에 대한 pointer를 얻을 수 있다.
  • Animation Instance 입장에서는 GetOwningActor()로 자신을 소유한 Actor 정보를 얻을 수 있다.
  • 이 두 함수를 통해 CharacterAnimInstance간에
    가 가능하다.

그렇다면 AnimInstance는 어떤 구조로 이루어져 있을까?

Event graph 영역과 Anim graph 영역

Event graph : 발생된 event로부터 상태를 파악할 수 있는 중요한 변수값을 저장하는데에 사용. AnimInstance가 초기화될때 실행되는 NativeInitializeAnimation함수와 매 frame마다 돌아가는 로직이 들어있는 NativeUpdateAnimation 함수가 존재한다.

Anim graph : Event graph에 저장된 변수를 가져와 사용. 이 변수들로 State machine의 state를 정하게 되는데 각 state에는 해당되는 animation이 있어서 이를 자동으로 play시켜주는 역할을 한다. Animgraph에서 우리가 정확하게 해줘야 할 할일은 어떤 state일때 어떤 animation이 재생될지를 정확하게 지정하는 것과, state과 state간의 transition 조건이다. 상황에 따라서 Anim graph는 매우 복잡하게 변할 수 있는데 이때 state alias와 Cache pose로 이를 효과적으로 분리해서 설계할 수 있다.


AnimInstance 헤더 파일

// AnimInstance.h
// Header에 선언한 이 변수들로 cpp에서 state를 결정하는 logic 구현.
#pragma once

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

/**
 * 
 */
UCLASS()
class ARENABATTLE_API UABAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
	
public:
	UABAnimInstance();

protected:
	virtual void NativeInitializeAnimation() override;

	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character)
	TObjectPtr<class ACharacter> Owner;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character)
	TObjectPtr<class UCharacterMovementComponent> Movement;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	FVector Velocity;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float GroundSpeed;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint8 bIsIdle : 1;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float MovingThreshould;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint8 bIsFalling : 1;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint8 bIsJumping : 1;

	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float JumpingThreshould;

};

이 AnimInstance를 상속해서 만든 Animation Blueprint를 열어보자. 여기서 톱니바퀴 -> 상속된 변수 표시를 하면 위에 header에서 UPROPERTY( ... BlueprintReadOnly ... )로 설정한 변수들을 getter로 가져와서 값을 읽을 수 있다.


AnimInstance의 중요한 두 함수

// AnimInstance.cpp
// AnimInstance가 초기화될때 1번 실행된다. 
void URyanAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();

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

// 매 frame마다 호출된다. 
// 여기서 AnimGraph의 state를 결정하는 변수들에 대한 값을 할당한다. 
void URyanAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	if (Movement)
	{
		Velocity = Movement->Velocity;
		GroundSpeed = Velocity.Size2D();
		bIsIdle = GroundSpeed < MovingThreshould;
		bIsFalling = Movement->IsFalling();
		bIsJumping = bIsFalling & (Velocity.Z > JumpingThreshould);
	}
}

AnimInstance.cpp에는 NativeInitializeAnimationNativeUpdateAnimation에 대한 로직을 작성한다. AnimInstance는 GetOwingActor()로 자신을 가지고 있는 Actor를 호출할 수 있고, 이 Actor의 property를 보고 AnimGraph에서 필요할 만한 변수들을 Actor로부터 가져온다. 여기서는 Character Movement에 대한 정보들이 필요하므로 GetOwingActor() -> Cast(체크용) -> GetCharacterMovement()로 가져온다.


본격적인 AnimGraph

Animation Blueprint가 상속하는 C++ AnimInstance 파일에서 완벽하게 변수들을 정해준다면 Animation Blueprint의 AnimGraph에서는 이 변수들을 getter로 접근해서 어떤 상황일때 어떤 Animation을 재생할지에 대한 로직을 구현한다.

이렇게 변수들이 준비되었다면 이제 State Machine을 통해 변수들이 어떤 상태일때 어떤 Animation을 보여줄지를 AnimGraph에서 결정하면 된다.

우선이 화면이 우리가 만들 최종 AnimGraph이다. StateMachine, BlendSpace, Cache, State Alias, Transition 등 다양한 개념이 등장한다.

Transition

Transition은 State간 이동할때 어떤 상황이어야지 다음 State로 갈 수 있는지에 대한 조건문이다.


BlendSpace

어떠한 변수의 증감에 따라 다른 애니메이션을 보여주고 싶을때 유용한 Animation 유형이다. 우리는 단일 변수(Character의 speed)로 걷기->경보->뛰기의 애니메이션을 보여주고 싶기 때문에 animation legacy 항목 중 BlendSpace Legacy 1D을 사용한다. 또한 이름에서 알 수 있듯이 변수의 증감에 따라 바뀌는 서로 다른 애니메이션 간에 transition이 자연스럽게 blending 된다.

BlendSpace를 만드는 방법은 Animation Blueprint를 만드는 것처럼 하나의 BS Asset을 만들고 이를 AnimGraph에서 사용하는데 마치 하나의 Animation Sequence처럼 사용하면 된다. 차이점이 있다면 BlendSpace는 변수에 따라 Animation이 변하므로 앞에 getter로 변수를 연결시켜 주어야 한다.

또한, BlendSpace를 구성한다음 Ctrl키를 누르고 마우스를 움직여보면 축 값에 따른 Animation의 재생이 어떻게 달라지는지 바로 확인할 수 있다.

AnimGraph에서 BS를 사용한 화면. BS_IdleWalkRun은 Ground Speed 변수에 의해 출력되는 animation pose가 다르다.


State Caching

Locomotion의 구성은 굉장히 복잡할 수 있기 때문에 State를 caching하는 방법을 많이 사용한다. Pose를 저장한다고 생각하면 되고 아주 직관적이다.

Main AnimGraph 화면은 위와 같은데, 여기서 로직의 분리를 위해 하나의 state machine을 더 추가했다. 이 state machine의 역할은 locomotion의 caching을 위함이고 Main state machine에서는 이 caching을 받는다(이는 Main state machine안에 caching을 받는 로직이 구현되어 있으므로 사진에서는 확인할 수 없다).


State Alias

State Alias는 자신이 체크한 State들에 대하여 자동으로 들어오게 되는 노드이다. 예를 들어서 State Alias인 ToJump는 Locomotion에서 자동으로 들어가는 노드이고 ToLand는 Jump 혹은 Falling State일때 자동으로 들어가게 되는 노드이다. State Alias에는 일반 State들과는 달리 정해진 Animation Sequence가 있지 않다.


최종화면

Quater View에서의 캐릭터 움직임 animation

Shoulder View에서의 캐릭터 움직임 animation

Shoulder View에서의 캐릭터 jumping animation

profile
🏦KAIST EE | 🏦SNU AI(빅데이터 핀테크 전문가 과정) | 📙CryptoHipsters 저자

0개의 댓글