게임 개발/언리얼 5

언리얼5 - 캐릭터 애니메이션 설정

싹난 감자 2024. 11. 26. 17:47

새로운 캐릭터 제작

프로젝트에 InfinityBlade 애셋 추가

Animation 폴더는 Content / ArenaBattle 아래에 추가

 

다양한 종류의 캐릭터가 있는데, 그중 Skeleton Asset을 중심으로 애니메이션을 설정함.

파일 경로가 달라지면 애니메이션 연결도 끊기고 스켈레톤도 제대로 열리지 않으니 주의.

BP_ABCharacterPlayer 블루프린트를 열고

스켈레탈 메쉬 컴포넌트를 보면 Use Animation Blueprint로 되어있음.

애니메이션 블루프린트 : Anim Class로 등록되어 있는 클래스 정보를 캐릭터가 생성될 때 해당 클래스로부터 애니메이션을 담당하는 인스턴스(언리얼 오브젝트)를 생성해서 관리시키도록 일임.

 

애니메이션 블루프린트는 Characters안에 만들어져있음.

이러한 애니메이션 블루프린트 시스템이 전체적인 캐릭터 모션을 담당.

 

AnimInstance를 상속받는 ABAnimInstance 생성

 

Content의 Animation 폴더에 Animation Blueprint 생성

캐릭터 애셋에 있던 스켈레톤 선택, Parent Class는 ABAnimInstance로 지정해 ABP_ABCharacter 생성

 

게임에서 사용중인 마네킹을 지우고 추가한 애셋 캐릭터로 변경하기

애니메이션을 사용할 애님 인스턴스에 대한 클래스, 블루 프린트에 대한 정보와 사용할 캐릭터를 지정해야함.

사용할 캐릭터의 스켈레탈 메쉬 레퍼런스 복사

    // 스켈레탈 메쉬 컴포넌트의 실제 애셋 부착
    // ThirdPersonCharacter에서 제공하는 메쉬와 클래스 사용
    static ConstructorHelpers::FObjectFinder<USkeletalMesh> CharacterMeshRef(TEXT("/Script/Engine.SkeletalMesh'/Game/InfinityBladeWarriors/Character/CompleteCharacters/SK_CharM_Cardboard.SK_CharM_Cardboard'"));
    if (CharacterMeshRef.Object)
    {
        // 스켈레탈에는 메쉬를 가져와서 등록
        GetMesh()->SetSkeletalMesh(CharacterMeshRef.Object);
    }

복사한 값으로 ABCharacterBase.cpp의 CharacterMeshRef 주소 변경

 

    static ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstanceClassRef(TEXT("/Game/ArenaBattle/Animation/ABP_ABCharacter.ABP_ABCharacter_C"));
    if (AnimInstanceClassRef.Class)
    {
        // 애니메이션에는 메쉬가 아니라 클래스 등록
        GetMesh()->SetAnimInstanceClass(AnimInstanceClassRef.Class);
    }

Animation 폴더에 생성했던 Animation Blueprint의 주소도 복사해서 ABCharacterBase.cpp의 AnimInstanceClassRef도 변경

 

빌드 후 실행하면 캐릭터가 변경된 것을 확인할 수 있음.

애니메이션 시스템을 지정은 했지만 내용이 없기 때문에 기본 자세로 움직임.

 


캐릭터 애니메이션 시스템의 생성

1. 스켈레탈 메시 컴포넌트의 애니메이션 블루프린트 클래스를 지정.

2. 캐릭터가 초기화될 때 AnimInstance 클래스의 인스턴스를 생성.

캐릭터는 GetAnimInstance 함수를 사용해 애니메이션 인스턴스를 얻을 수 있음.

애니메이션 인스턴스는 GetOwningActor 함수를 사용해 자신을 소유한 액터 정보를 얻을 수 있음.

 

캐릭터 애니메이션 시스템의 설계

애니메이션 블루프린트는 이벤트 그래프와 애님 그래프의 두 영역으로 구성됨.

이벤트 그래프는 이벤트로부터 상태를 파악할 수 있는 주요 변수를 저장하는데 사용

  • NativeInitalizeAnimation : 처음 애니메이션 시스템이 초기화될 때 발생하는 이벤트
  • NativeUpdateAnimation : 프레임마다 발생하는 이벤트
  • 이벤트가 발생하면 GetOwningActor()를 사용해 캐릭터의 정보를 가져와서 현재 캐릭터의 상태를 지정한 변수에 저장.

애님 그래프는 저장된 변수로부터 지정된 상태의 애니메이션을 재생

  • State Machines이라는 미리 설계한 상태를 중심으로 애니메이션을 자동으로 재생

애님 그래프의 복잡한 상태는 State Alias로 분리해 효과적으로 설계할 수 있음.


애니메이션 구현

Land Mode와 Falling Mode에 필요한 애니메이션을 분리해서 구현

애니메이션 블루프린트에 원하는 모션 넣기

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

#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:
	// 주요 이벤트 함수
	// AnimInstance가 처음 생성될 때 한번 호출
	virtual void NativeInitializeAnimation() override;
	// 프레임마다 호출
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

	// AnimGraph가 참조할 변수

	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;

	// Idle 상태인지 판단
	// bool 타입은 사이즈가 어떻게 될지 명확하지 않음
	// 명확한 사이즈를 위해 정수형으로 선언
	// 접두사 b 붙이고 비트 플래그 추가
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint32 bIsIdle : 1;

	// 움직이고 있는지 쉬고 있는지 나타냄
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float MovingThreshold;

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

	// 점프하고 있는지 판단
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint32 bIsJumping : 1;

	// 현재 점프 중인지 나타냄
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float JumpingThreshold;
};

ABAnimInstance.h

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


#include "ABAnimInstance.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"

UABAnimInstance::UABAnimInstance()
{
    MovingThreshold = 3.0f;
    JumpingThreshold = 100.0f;
}

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

    // 오브젝트에 대한 포인트 값 초기화
    // GetOwningActor는 Actor로 반환되어 캐릭터인지 알 수 없음.
    // 캐릭터로 형 변환
    Owner = Cast<ACharacter>(GetOwningActor());
    if (Owner)
    {
        Movement = Owner->GetCharacterMovement();
    }
}

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

    // NativeInitializeAnimation에서 얻어온
    // Movement 객체로부터 원하는 값 얻기
    if (Movement)
    {
        Velocity = Movement->Velocity;
        // Velocity에서 z제외 x,y값
        GroundSpeed = Velocity.Size2D();
        bIsIdle = GroundSpeed < MovingThreshold;
        bIsFalling = Movement->IsFalling();
        bIsJumping = bIsFalling & (Velocity.Z > JumpingThreshold);
    }
}

ABAnimInstance.cpp

 

애니메이션 블루프린트에서 Show Inherited Variables 옵션을 켜면 상속받은 변수를 볼 수 있음.

이 변수들을 사용해서 애님 그래프를 설계하고 구현.

 

애님 그래프 창이 없을 경우 My Blueprint에서 AnimGraph 더블 클릭

 

애님 그래프에서 State Machine 노드 생성, Ouput Pose에 연결. 스테이트 머신의 결과가 나가게 됨

 

State Machine 노드 더블클릭해서 설계 진입.

우클릭해 State를 추가하고 Locomotion이라는 이름으로 지정 후 Entry와 연결.

로코모션(Locomotion)

주로 한 곳에서 다른 곳으로 이동할 때 발생되는 다양한 캐릭터의 움직임에 사용되는 모션, 기법, 테크닉을 지칭.
크게는 움직임 자체를 로코모션이라고 하고, 다양한 지형 지물을 접할 때 변화되는 세부적인 팔과 다리의 움직임을 지칭하기도 함.
스테이트 머신에 사용된 용어는 전자를, 루트 모션 모션 워핑 등의 애니메이션 와핑에서 사용하는 용어는 후자.

 

로코모션 스테이트에서 구현할 애니메이션 지정.

 

이러한 로코모션은 굉장히 복잡할 수 있어 로코모션에 대한 구성을 다른 곳에서 진행하고 결과를 캐시(Cache)로 저장해서 불러오는 방법이 있음.

 

걷고 뛰는 모션 구현

AnimGrap에 새로운 스테이트 머신 노드를 Locomotion이라는 이름으로 추가

추가한 Locomotoin 스테이트 머신 노드를 열어서 상태 추가

Idle에서 IdleWalkRun으로 연결되는 선을 더블클릭

 

IsIdle 변수를 끌어와서 Not Boolean 조건일 때만 상태를 전이하도록 설정.

반대의 경우에는 IsIdle일 때 전이하도록 설정.

 

돌아와서 스테이트 머신 노드의 Idle 스테이트 노드를 열고 모션을 끌어와 추가하고 Result에 연결.

IdleWalkRun 상태는 여러 모션이 GroundSpeed에 따라 혼합되어 보여져야 함.

혼합된 모션을 추가.

Animation 폴더에서 우클릭 - Animation - Legacy - Blend Space 1D, 지정할 캐릭터 스켈레톤을 선택하고 BS_IdleWalkRun이라는 이름으로 생성 (BS: 블렌드 스페이스)

 

가로축에 대한 값 설정.

이름은 GroundSpeed, 최대 값 500으로 지정.

 

사용할 3가지 모션들을 끌어서 배치

각 모션의 GroundSpeed 값 설정. (0, 230, 500)

 

ctrl을 누르고 마우스를 움직여보면 X 표시가 움직이며 GroundSpeed 값에 따라 모션이 혼합되어 재생되는 것을 볼 수 있음.

 

애니메이션 블루프린트로 돌아와서 IdleWalkRun State에 새로 만든 모션을 끌어서 추가.

GroundSpeed 변수와 BS 모션과 Output을 연결.

 

완료된 Locomotion State를 캐시로 저장.

 

MainStateMachine 안에 있는 Locomotion State를 열어 저장된 로코모션 캐시값을 사용.

저장 후 컴파일.

Locomotion에서 만들어져서 저장된 LocomotionCache 포지션을 MainStateMachine에서 바로 재생하고 최종 결과물에 보내줌.

게임을 실행해보면 모션이 잘 나오는 것을 확인할 수 있음.

 

Jump 모션 구현

MainStateMachine의 Locomotion은 Ground에 있을 때.

Folling Mode일 때 구현해야할 상태도 추가.

 

Locomotion에서 시작해서 FallingMode로 진행할 새로운 Alias(ToJump) 생성.

ToJump는 Locomotion일 때 전환됨. Locomotion 옵션 체크

ToJump는 Jump와 Falling 상태로 연결.

Jump는 IsJumping일 때 전이, Falling은 IsFalling일 때 전이.

Jump 상태는 Jump 모션, Falling 상태는 떨어지는 루프 모션 수행.

Jump와 Falling 사이에서는 Jump가 우선됨.

ToJump에서 Jump로 가는 Transtion 조건에 Priority Order값을 더 크게 설정하면 두 가지 조건을 모두 만족할 때 Jump 상태가 우선권을 가지게 됨.

 

Jump가 끝난 후엔 Falling으로 전이. Jump에서 Falling으로 전이될 때 ratio(비율) 조건을 추가해 Jump 애니메이션이 얼마 남지 않았을 때 Falling으로 잘 전환되도록 설정.

Jump 모션이 10% 남았을 때 전환을 시작하도록 설정.

 

착지하는 동작을 추가하기 위해 ToRand라는 Alias를 추가. Jump와 Falling 상태에서만 도달할 수 있도록 체크.

ToLand에서는 Land로 전이, Land State에서 Land 모션 실행.

Land 모션은 Locomotion과 연결되어야 하기 때문에 캐시로 저장된 로코모션과 가산 형태의 혼합으로 진행하도록 설정.

 

Land모션이 끝나면 Locomotion으로 이어지도록 연결, Jump처럼 10%가 남았을 때 전이되도록 설정.

컴파일 후 실행하면 점프 모션도 잘 나오는 것을 확인할 수 있음.

애니메이션 블루프린트 작업이 끝났으니 Content 폴더 안의 더 이상 사용하지 않을 ThirdPerson 템플릿과 클래스를 삭제.

ArenaBattle 모듈 폴더의 GameModeBase도 사용하지 않기 때문에 삭제 후 VS 프로젝트 재생성.

빌드 후 게임을 실행해보면 잘 동작한다.


애님 그래프 보충

State Alias

애님 그래프의 Alias를 사용하지 않고 State만 사용해서 애니메이션을 구현할 수도 있으나 간선이 꼬이고 복잡해 보기 힘듦. Alias는 애님 그래프 작성 시 공통되는 정점이나 간선을 묶어주는, 노드 정리를 간편하게 해주는 추상적 개념의 노드.

 

애님그래프의 C++ 구현

애님 그래프를 C++로 구현하는 것도 가능은 할 듯하지만, 애니메이션의 변화를 보면서 바로 바로 모델 설계를 변경해야 하기 때문에 C++로 만드는 경우는 거의 없음.

 

애니메이션 시스템은 이펙트와 유사하게 시각적인 효과를 담당하기에, 애니메이션까지 다룬다면 모션 애셋을 바탕으로 어떻게 더 시각적으로 훌륭한 결과물을 만들지를 고민하고 깊게 들어가야 함.


이는 언리얼 엔진이 제공하는 나이아가라를 활용해 이펙트를 어떻게 더 잘 만들지에 대한 내용과도 유사하며, 이펙트 전문 과정이 있듯이 애니메이션도 이것만 전문으로 다루는 과정이 있어야.

애니메이션과 유사하게 분류 할 수 있는 이펙트 관련된 교육은 종종 보이지만, 애니메이션 시스템을 설계하는 내용은 프로그래머와 애니메이터 사이의 사각지대.

 

절차적 애니메이션(Procedural animation)의 경우에도 프로그래머가 구현하느냐, 애니메이터가 미리 만들어 놓느냐에 대한 경계가 모호한 것고 유사한 듯.

 


블루 프린트를 열었을 때 빈 화면만 뜨는 경우

Window - Class Defaults 창 열기

Class Defaults 창이 열리면 파란색 Open Full Blueprint Editor클릭.

전체 설정 확인 가능.

 

 


https://www.inflearn.com/course/%EC%9D%B4%EB%93%9D%EC%9A%B0-%EC%96%B8%EB%A6%AC%EC%96%BC-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-part-2/dashboard

 

이득우의 언리얼 프로그래밍 Part2 - 언리얼 게임 프레임웍의 이해 강의 | 이득우 - 인프런

이득우 | 대기업 현업자들이 수강하는 언리얼 C++ 프로그래밍 전문 과정입니다. 언리얼 C++ 프로그래밍을 사용해 핵&슬래시 로그라이크 게임 예제를 처음부터 끝까지 체계적으로 제작하는 방법을

www.inflearn.com