스테이트 머신과 애니메이션 Blueprint 연동
C++의 이동·공중·공격 상태를 Animation Blueprint 변수와 스테이트 머신 전이 조건에 연결합니다.
이전 절에서 컴포넌트 시스템을 이해하고 활용하는 방법을 배웠습니다.
이제 우리 캐릭터에게 생동감을 불어넣기 위해 애니메이션을 제어하는 방법에 대해 알아보겠습니다.
특히, 복잡한 캐릭터의 동작을 효율적으로 관리하고 애니메이션을 매끄럽게 전환하는 데 필수적인 개념인 스테이트 머신(State Machine)과 이를 언리얼 엔진에서 구현하는 애니메이션 블루프린트(Animation Blueprint) 연동에 초점을 맞출 것입니다.
이 예제는 C++에서 계산한 평면 속도와 낙하 상태를 애니메이션 블루프린트로 전달합니다. 공격 플래그는 후속 구현을 위한 자리이며, 현재 코드에는 공격 상태를 갱신하는 로직이 없습니다.
스테이트 머신(State Machine)이란 무엇인가요?
스테이트 머신은 어떤 시스템이나 객체가 가질 수 있는 유한한 수의 '상태(State)'와, 한 상태에서 다른 상태로 전환될 수 있는 전이(Transition)로 구성된 모델입니다.
특정 이벤트가 발생하면 현재 상태에서 정의된 규칙에 따라 다른 상태로 전이됩니다.
게임 캐릭터 애니메이션에서 스테이트 머신은 상태와 전환 조건을 분리해 관리하는 방식입니다.
예를 들어, 캐릭터는 다음과 같은 상태를 가질 수 있습니다.
- Idle (대기): 아무것도 하지 않고 서 있는 상태.
- Walk (걷기): 걷고 있는 상태.
- Run (달리기): 뛰고 있는 상태.
- Jump (점프): 점프 중인 상태.
- Attack (공격): 공격 중인 상태.
각 상태는 고유한 애니메이션을 재생하며, 특정 조건(예: 이동 입력 감지, 점프 버튼 누름, 공격 버튼 누름)이 만족되면 다른 상태로 전이됩니다.
스테이트 머신을 사용하면 이러한 복잡한 애니메이션 흐름을 명확하고 체계적으로 관리할 수 있습니다.
애니메이션 블루프린트
언리얼 엔진은 스테이트 머신을 포함한 복잡한 애니메이션 로직을 시각적으로 구현할 수 있도록 애니메이션 블루프린트를 제공합니다.
애니메이션 블루프린트는 호환되는 스켈레톤을 사용하며, 스켈레탈 메시 컴포넌트의 Anim Class로 지정되어 해당 스켈레탈 메시를 가진 캐릭터의 애니메이션을 제어합니다.
애니메이션 블루프린트의 주요 부분은 다음과 같습니다.
- 캐릭터의 스켈레탈 메시의 최종 포즈를 계산하고 적용하는 핵심 그래프입니다.
- 스테이트 머신(State Machine) 노드가 주로 여기에 위치하여 캐릭터의 주요 동작 상태를 정의하고 관리합니다.
- 여러 애니메이션 클립(Sequence)을 블렌딩하거나, 부분적으로 애니메이션을 적용하는 등의 복잡한 작업을 수행합니다.
- 애니메이션 블루프린트가 동작하는 동안 발생하는 이벤트(예:
Event Blueprint Update Animation)를 처리하고, C++ 코드나 다른 블루프린트로부터 전달받은 변수들을 사용하여 애니메이션 로직을 업데이트합니다. - 여기서 캐릭터의 속도, 점프 상태, 공격 상태 등 게임플레이 관련 변수들을 가져와 애님 그래프의 스테이트 머신에 전달합니다.
C++와 애니메이션 블루프린트의 연동
C++에서 캐릭터의 이동 속도나 점프 상태와 같은 게임플레이 데이터를 계산한 후, 이 데이터를 애니메이션 블루프린트로 전달하여 올바른 애니메이션이 재생되도록 해야 합니다.
이 과정은 주로 UPROPERTY 매크로를 사용하여 C++ 변수를 블루프린트에 노출하고, 애니메이션 블루프린트의 이벤트 그래프에서 해당 변수를 읽어 애님 그래프의 스테이트 머신에 전달하는 방식으로 이루어집니다.
애니메이션 관련 변수 선언
ACharacter를 상속받는 우리의 AMyCharacter 클래스에서 애니메이션에 필요한 변수들을 UPROPERTY로 선언합니다.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
// ... (기존 헤더들) ...
#include "MyCharacter.generated.h"
UCLASS()
class MYFIRSTCPPPROJECT_API AMyCharacter : public ACharacter
{
GENERATED_BODY()
public:
AMyCharacter();
protected:
virtual void BeginPlay() override;
public:
// ... (기존 Tick, SetupPlayerInputComponent 등) ...
protected:
// ... (기존 Input 관련 UPROPERTY 변수들) ...
// 애니메이션 블루프린트에 노출할 변수들
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Animation")
float CurrentSpeed; // 현재 이동 속도
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Animation")
bool bIsInAir; // 공중에 떠 있는지 여부
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Animation")
bool bIsAttacking; // 공격 중인지 여부 (예시)
// 애니메이션 블루프린트에 필요한 데이터를 업데이트하는 함수 (Tick에서 호출)
void UpdateAnimationVariables();
};애니메이션 관련 변수 업데이트
MyCharacter.cpp 파일의 Tick 함수 또는 별도의 함수에서 이 변수들의 값을 업데이트합니다.
#include "MyCharacter.h"
// ... (기존 헤더들) ...
#include "GameFramework/CharacterMovementComponent.h" // GetCharacterMovement()를 위해 포함
AMyCharacter::AMyCharacter()
{
// ... (기존 생성자 로직) ...
}
void AMyCharacter::BeginPlay()
{
Super::BeginPlay();
// ... (기존 BeginPlay 로직) ...
}
void AMyCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// 매 프레임 애니메이션 관련 변수 업데이트
UpdateAnimationVariables();
}
void AMyCharacter::UpdateAnimationVariables()
{
// 현재 이동 속도 계산 (XY 평면 속도)
CurrentSpeed = GetCharacterMovement()->Velocity.Size2D();
// 공중에 떠 있는지 여부
bIsInAir = GetCharacterMovement()->IsFalling();
// 공격 상태는 추후 공격 시스템 구현 시 설정 (예시)
// bIsAttacking = ...;
}스켈레탈 메시, 애니메이션 블루프린트 설정
스켈레탈 메시 에셋: 언리얼 엔진 프로젝트에 사용할 캐릭터의 스켈레탈 메시 에셋이 준비되어 있어야 합니다. (예: SK_Mannequin 또는 외부 임포트 모델)
애니메이션 에셋: Idle, Walk, Run, JumpStart, JumpLoop, JumpEnd 등의 애니메이션 시퀀스 에셋이 필요합니다.
- 콘텐츠 브라우저에서 사용할 스켈레탈 메시를 찾아 마우스 오른쪽 버튼 클릭 >
Create>Anim Blueprint를 선택합니다. - 이름을
ABP_MyCharacter와 같이 지정합니다.
이벤트 그래프에서 C++ 변수 가져오기
ABP_MyCharacter를 더블 클릭하여 애니메이션 블루프린트 에디터를 엽니다.
Event Blueprint Update Animation 노드를 찾거나 생성합니다.
이 이벤트는 애니메이션 업데이트 시 호출됩니다. 가시성·최적화·틱 설정에 따라 갱신이 생략될 수 있으므로 항상 모든 게임 프레임과 일대일이라고 가정하지 않습니다.
이벤트 그래프에 여러분의 C++ 캐릭터 클래스(AMyCharacter)의 인스턴스를 가져옵니다.
Try Get Pawn Owner의 반환값이 유효한지 확인한 뒤 Cast To MyCharacter를 사용합니다. 프리뷰나 다른 소유자에서는 null 또는 캐스트 실패가 가능하므로 성공 경로에서만 변수를 읽습니다.
가져온 폰(AMyCharacter 레퍼런스)에서 드래그하여 Get Current Speed, Get b Is In Air, Get b Is Attacking 노드를 추가합니다. (C++에서 UPROPERTY(BlueprintReadOnly)로 선언했으므로 자동으로 Getter 노드가 생성됩니다.)
이 값들을 애니메이션 블루프린트 내부의 변수에 저장합니다.
Set Current Speed, Set Is In Air 등의 노드를 사용하여 애니메이션 인스턴스의 멤버 변수를 업데이트합니다.
이 멤버 변수들은 애님 그래프의 스테이트 머신에서 사용합니다.
애님 그래프에서 스테이트 머신 구축
결과 포즈(Result Pose) 노드 옆에 State Machine (스테이트 머신) 노드를 추가하고 연결합니다.
스테이트 머신 노드를 더블 클릭하여 내부로 들어갑니다.
- Entry 노드에서 드래그하여
Idle상태를 추가합니다. Idle에서 드래그하여Walk_Run상태를 추가합니다.Walk_Run에서 드래그하여Jump상태를 추가합니다. (또는 별도의 점프 시작/루프/착지 상태를 만들 수도 있습니다)- 필요하다면
Attack상태 등도 추가합니다.
- 각 상태 노드를 더블 클릭하여 들어갑니다.
Idle상태에는Idle애니메이션 시퀀스 에셋을 끌어다 놓고 결과 포즈에 연결합니다.Walk_Run상태는Blend Space 1D를 사용하여Walk와Run애니메이션을 속도에 따라 블렌딩하도록 설정합니다. (블렌드 스페이스를 미리 생성하고,CurrentSpeed변수를 X축에 연결합니다.)- 먼저 Jump 상태에는 반복 재생할 JumpLoop 하나를 결과 포즈에 연결합니다. 시작·착지 클립까지 쓰려면 JumpStart·JumpLoop·JumpEnd를 별도 상태로 만들고 재생 종료와 착지 조건으로 전이시킵니다. 한 상태 안에 Sequence 세 개를 늘어놓는 것만으로 순차 재생되지는 않습니다.
- Idle과 Walk_Run 사이에 양방향 전이를 만듭니다.
- Idle과 Walk_Run 각각에서 Jump로 가는 전이를 만듭니다. 공통 규칙이 필요하면 Conduit나 State Alias를 구성할 수 있지만, 이 실습은 명시적인 연결을 사용합니다.
- Jump에서 Idle과 Walk_Run으로 돌아오는 전이를 만들고, 아래 조건을 각 목적지의 전이에 적용합니다.
공중 여부로 먼저 영역을 나누고 지상에서만 속도 임계값을 비교합니다.
| 목적 상태 | 들어갈 조건 | 원문 값의 의미 |
|---|---|---|
| Jump | bIsInAir가 true | IsFalling 결과; 모든 수영·비행 상태를 뜻하지 않음 |
| Idle | bIsInAir가 false이고 CurrentSpeed < 10 | XY 평면 속도; 기본 길이 단위에서는 cm/s |
| Walk_Run | bIsInAir가 false이고 CurrentSpeed >= 10 | 이 값으로 Blend Space의 보행·달리기 포즈 혼합 |
- Jump
- 들어갈 조건: bIsInAir가 true원문 값의 의미: IsFalling 결과; 모든 수영·비행 상태를 뜻하지 않음
- Idle
- 들어갈 조건: bIsInAir가 false이고 CurrentSpeed < 10원문 값의 의미: XY 평면 속도; 기본 길이 단위에서는 cm/s
- Walk_Run
- 들어갈 조건: bIsInAir가 false이고 CurrentSpeed >= 10원문 값의 의미: 이 값으로 Blend Space의 보행·달리기 포즈 혼합
10은 실습용 임계값입니다. Walk_Run 포즈가 달라지는 것과 실제 이동 속도 설정은 별개이며, 공격 전이는 아직 구현하지 않았습니다.
공격·피격이나 착지 클립을 추가할 때는 포즈 잠금과 전이 우선순위를 별도로 정합니다. 남은 재생 시간 조건은 해당 Sequence와 루프 설정에 맞춰 사용하며, 우선순위 지정만으로 모든 상태 떨림이 사라지는 것은 아닙니다.
이 과정을 통해 여러분은 C++에서 계산된 게임플레이 변수들을 애니메이션 블루프린트로 전달하고, 이 변수들을 사용하여 캐릭터의 복잡한 애니메이션 상태를 제어하는 스테이트 머신을 구축하게 됩니다.
최종 연동 확인
마지막 확인은 메시, 애님 클래스, 이벤트 그래프 변수, 전이 규칙이 모두 같은 캐릭터 인스턴스를 바라보는지 검증하는 과정입니다.
캐릭터 블루프린트에서 스켈레탈 메시 설정:
여러분의 BP_MyCharacter 블루프린트를 열고, USkeletalMeshComponent (또는 ACharacter의 기본 Mesh 컴포넌트)를 선택합니다.
- Skeletal Mesh 항목에 사용할 스켈레탈 메시 에셋을 할당합니다.
- Anim Class 항목에 생성한
ABP_MyCharacter애니메이션 블루프린트를 할당합니다.
플레이 및 테스트:
언리얼 에디터에서 Play 버튼을 눌러 게임을 실행합니다.
이제 WASD 키로 이동하면 캐릭터가 걷거나 달리는 애니메이션으로 자연스럽게 전환되고, 스페이스바를 누르면 점프 애니메이션이 재생되는 것을 확인할 수 있을 것입니다.
테스트할 때는 Debug Object에서 실제 조작 중인 캐릭터의 Anim Instance를 선택합니다. Pawn Owner와 캐스트, C++ 값, Anim 변수, 활성 전이를 차례로 확인합니다. Character Tick과 애니메이션 업데이트 사이의 순서가 필요하면 틱 의존성도 명시해야 합니다.