Unreal

AnimationBlueprint 를 이용해 캐릭터의 움직임을 표현해보기 - Unreal Engine

workbench34 2025. 2. 6. 20:09

 저번에 캐릭터를 C++ 을 통해 움직이거나 마우스로 캐릭터를 조종하는 방법을 알아보았다. 그러나 언제까지고 캐릭터가 멈춰선 채로 움직이는 것을 볼 수 없다. 이제부터 캐릭터에게 생동감을 보여줄 애니메이션을 넣어보도록 하자.

 

 일단 Unreal Engine 에는 AnimationBluePrint 라는게 존재한다. 편의상 ABP 로 줄여보도록 하겠다. 이는 특정 스켈레탈 매시에 대해 움직임을 적용시켜주는 블루프린트이다. ABP 는 생성 시, 스켈레톤 (Skeletion)과 부모 클래스를 선택해야하는데 이때 스켈레탈 매시는 원하는 것으로 지정해주고 그 뒤 Parent Class (부모 클래스) 는 보통 AnimInstance 를 선택해준다.

컨텐츠 드로우에 우클릭 하면 다음과 같이 뜬다.

 

 나는 저번에 만들었던 캐릭터의 스켈레탈 매시를 선택하겠다.

 

 이름은 ABP_캐릭터 이름 을 붙여주도록 하자.

 

 ABP 를 살펴보면 크게 두 가지의 그래프를 확인할 수 있다. 첫 번째는 Animp Graph 이고 두 번째는 Event Graph 이다. 더 자세히 알아보자.

 

Anim Graph (애니메이션 그래프)

 입력노드 → 블렌딩 (Blend) → 출력 노드 (Output Pose) 순으로, 최종적으로 캐릭터가 어떤 포즈와 애니메이션을 취할지 결정한다. 예시로 캐릭터가 걷기 에서 달리기 로 전환할때, 얼마나 부드럽게 섞어줄지, 또 속도에 따라 Walk 애니메이션과 Run 애니메이션의 비율을 어떻게 조절할지를 정의한다.

 

Event Graph (애니메이션 그래프)

 일반 블루프린트와 유사한 이벤트 그래프이다. Tick 이벤트나 BlueprintImplementableEvent 등을 활용해 C++ 혹은 게임 로직과 애니메이션을 연동할 수 있다. 예를 들어 공격 모션 중 특정 타이밍에 데미지를 가한다든지, Idle 상태가 5초 이상 지속될 경우 자동으로 '긴장 풀린' 애니메이션을 재생하는 식의 이벤트 로직을 추가할 수 있다.

 

 이제 두 차이를 알았으니 Anim Graph 부터 살펴보자. Anim Graph 에서는 애니메이션을 연결해 왼쪽 창에 어떤 형태로 보여질지 볼 수 있다. 좌측 하단의 에셋 브라우저에서 시퀀스를 끌어와 그래프 안에 배치시킨 뒤 Output Pose 에 연결시키면 쉽게 확인할 수 있다.

 

 

 간단하게 Idle 을 끌어와 연결해보자. 그러면 왼쪽 위 화면에 캐릭터가 기본 상태로 서 있는 것을 확인할 수 있다. 여기서 재생시간이 끝나면 캐릭터가 멈추게 되는데 이를 방지하고자 디테일 (Details) 창의 애니메이션 루프 (Loop Animation) 을 체크해주면 Idle 동작이 지속적으로 반복 재생된다.

 

 이제 캐릭터 블루프린트에 적용해 보자. 전에 만들었던 캐릭터 블루프린트 BP_Character 를 열고, Mesh 컴포넌트를 선택한다. 그 뒤 Details 창에서 Animation 항목의 Animation Mode 를 "Use Animation Blueprint" 로 설정해주고, Anim Class 에 우리가 만든 ABP_Character 를 할당해준다.

 저장 후 언리얼 에디터에서 실행 해보면 Idle 상태로 캐릭터가 움직이는 것을 볼 수 있다.

 

 

 그러나 아직 Idle 상태로만 움직인다. 우린 캐릭터가 직접 뛰고 움직이기를 원한다. 그렇게 만드려면 이제 캐릭터의 움직이는 속도 즉, Character Movement Component 를 변수화 해 이 수치에 따라 캐릭터가 움직임을 보이게 만들어야한다. ABP 의 Event Graph 로 돌아가보자. 그 뒤 아래와 같이 블루프린트를 만들어 준다. 여기서 난 내가 만든 캐릭터 의 BP 를 캐스트 하였으나 C++ 에서 Character Movement 를 직접 설계한 것이 아닌, 언리얼에서 만들어진 움직임으로 적용했으면 그냥 Cast to Character 을 사용해도 된다.

 

 

 Set 들은 끌어와서 promote to variable 을 눌러주면 자동으로 Set 이 된다. 이제 캐릭터의 속도를 가져와 이에 따라서 움직임을 만들어야 하는데 만약 직접 움직임을 만든게 아닌 AddMovementInput 로 처리했다면 여기서 그냥 Character Movement 를 변수로 사용해도 된다. 하지만 난 직접 움직임을 구현했기에 이에 따른 변수를 C++ 에서 선언 해 주어야한다. 다음과 같은 코드들을 변경했다.

 

public:

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "CharaterStatus")
	FVector CurrentVelocity;

 

 헤더파일 public 에 CurrentVelocity 라는 블루프린트에서 참조 가능한 FVector 을 생성해 주었다.

 

void ATistoryCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	if (Controller)
	{
		FVector CurrentLocation = GetActorLocation();
		FVector NextLocation = GetActorLocation();
		if (MoveForwardInput != 0.0f)
		{
			FVector ForwardVector = GetActorForwardVector() * MoveForwardInput * CurrentSpeed * DeltaTime;
			CurrentLocation += ForwardVector;
		}

		if (MoveRightInput != FVector2D::ZeroVector)
		{
			FVector RightVector = GetActorRightVector() * MoveRightInput.Y * CurrentSpeed * DeltaTime;
			CurrentLocation += RightVector;
		}

		SetActorLocation(CurrentLocation, true); // true 이게 있으면 앞에 벽이 있어 움직이지 못할 것 같다 싶으면 아예 움직이는 모션도 안나오게 한다
		CurrentVelocity = (CurrentLocation - NextLocation) / DeltaTime;
	}
}

 

 cpp 파일 에는 Tick 함수를 좀 변경하여 CurrentVelocity 가  현재 장소와 전의 장소를 구별하여 빼준 뒤 그를 DeltaTime으로 나누어 속력을 가져오게 하였다. 이렇게 캐릭터의 속도를 이제 알 수 있게 되었다. 그럼 다시 블루프린트로 넘어가 보자. 아까 만든 블루프린트 아래에 다음과 같이 추가해준다.

 

 

 위의 Get 은 다음과 같이 생성할 수 있다.

이제 Velocity 를 가져와 속도를 표현해주는 Ground Speed 를 만들어보자. 일단 Ground Speed 라는 float 변수를 만들어준다.

 

 그 뒤 다음과 같이 만들어 준다.

 

 난 편의상 tab 을 만들어 Ground Speed 라 이름을 붙여주었다. Current Velocity 는 get BP_Character 에서 끌어와 Current (아니면 만들어 두었던 변수명)라 검색하면 바로 찾을 수 있다.

 

 이 다음에는 매 프레임마다 캐릭터의 움직임 여부를 저장하는 Bool 변수를 만들 것이다. 그 뒤 이에 관련된 로직을 구성해주자. 먼저 변수에 bSouldMove 를 bool 형으로 만들어주자.

 

 그 뒤 다음과 같이 로직을 구성해 준다. 만약 Character Movement 를 사용할 거면 아래의 Current Velocity 대신 Get Current Accleration 노드를 생성해 Character Movement 에 연결해 사용하면 된다. (아래의 !== 노드에 연결하면 된다.)

 

 

 원래는 점프 애니메이션을 넣는다면 그것까지 고려해 세개로 만들어야하나 우린 점프 애니메이션이 없기에 이는 따로 만들지 않겠다. 이제 Anim Graph 에서 Locomotion 이라는 State Machine 을 설계하자.

 

 State Machine 이라는 단어는 생소할 것이다. 이는 언리얼 엔진의 애니메이션 시스템에서, 캐릭터의 상태(Idle, Walk, Run, Jump)에 따라 어떤 애니메이션을 재생하고, 어떻게 전환할지를 결정하기 위한 논리적 구조이다. 가장 쉽게 이해하자면, “Idle 상태이면 Idle 애니메이션을 재생하고, 캐릭터가 움직이기 시작하면 Walking 애니메이션으로 전환한다.” 같은 로직을 직관적으로 구성하게 해준다.

 

 핵심 개념으로는

 

State

 캐릭터가 현재 어떤 동작을 하고 있는지 나타낸다. 예로 Idle, Walk, Runnig, Jump 등이 하나의 상태가 될 수 있다.

Trasition

 한 상태에서 다른 상태로 언제 전환되는지 조건을 정의한다. 예로 Idle -> Walking 의 전환조건 : 속도 > 1.0 (캐릭터가 이동을 시작하면)

 

 이제 Anim Graph 로 돌아가 State Machine 을 생성해보자.

 

 

 위와 같이 생성하여 Locomotion 이라 이름을 붙여주겠다. 이를 더블클릭 해보면 Entry 노드가 보인다. 이를 끌어와서 스테이트 추가 (Add State) 를 선택해 준 뒤 Idle 이라 이름을 붙여준다.

 

 Idle 을 더블 클릭 해보면 Anim Graph 의 처음 모습처럼 Output Pose 가 보일 것이다. 여기에다가 에셋 브라우저의 Tutorial_Idle 을 끌어와 연결해준다. 여기서 Tutorial_Idle 디테일 창에 애니메이션 루프를 체크하는 것을 잊지 말자. 위에서 설명했으니 자세한건 위에서 다시 보고 오자.

 

 이제 Locomotion 으로 돌아와 Idle 에서 끌어와 다시 스테이트를 추가해준다. 이 스테이트는 Run 으로 이름붙여주자. 다시 Run State 에서 Idle State 로 드래그 해주면 다음과 같이 Trasintion 이 두개 생긴다.

 Run 으로 들어가 위에서 Idle 을 Output Pose 에 연결한 것 처럼 Tutorial_Walk_Fwd 를 연결해준다. 이때에도 애니메이션 루프를 설정해주자. 그 뒤 위의 트랜지션은 아래와 같이 만들어 준다.

 

 아래의 트랜지션은 아래와 같이 만들어준다.

 

 그 뒤 컴파일 해준 후 Anim Graph 로 돌아가 Locomotion 과 Output Pose 를 연결해준다.

 

 이제 언리얼 에디터로 돌아가 캐릭터를 움직여보면 적용이 된 사실을 알 수 있다!

 

 아직은 어색하지만 그래도 캐릭터의 애니메이션은 적용이 되었다. 다음에는 점프와 낙하 애니메이션 그리고 앉기 와 앉아서 이동하기 등을 만들어 보겠다.