언리얼엔진/FPS 프로젝트

[UE5 | FPS] 애니메이션 블루프린트 링크

mstone8370 2024. 5. 7. 15:45

 

 

현재 진행중인 FPS 프로젝트는 플레이어 캐릭터의 팔 메시는 하나만 존재한다.

팔 애니메이션은 현재 들고있는 무기에 따라서 달라져야하는데 이때에는 무기에 맞는 애니메이션 블루프린트로 교체하는 방식을 사용한다.

부모 애니메이션 블루프린트에서 노드 그래프를 작성하고, 각 무기마다 자식 블루프린트를 만들어서 에셋 오버라이드를 통해 무기에 맞는 애니메이션을 설정한다.

각 무기에 맞는 애니메이션을 각자의 블루프린트에서 관리하니 한눈에 보기에 좋고, 새로운 무기가 추가될 때에도 자식 블루프린트를 만들어서 에셋만 지정해주면 되니 복잡하지 않다.

 

하지만 이 방법은 애니메이션 블루프린트가 교체될때마다 포즈가 초기화되면서 한 프레임씩 기본 포즈가 나타나는 문제가 있다.

무기를 집어넣고 꺼내는 애니메이션은 몽타주를 재생하는 방식으로 표현하고있어서 기본 포즈는 Idle 상태의 포즈다.

만약 무기를 집어넣는 몽타주가 끝나서 애니메이션 블루프린트가 교체되면 교체된 무기를 들고있는 Idle 포즈가 한 프레임 나타났다가 해당 무기를 화면 밖에서 꺼내는 몽타주가 재생된다.

 

전에 만든 프로젝트에서도 동일한 방식을 사용해서 예시를 가져와봤다.

 

예전에 만들었던 프로젝트

무기가 교체되는 순간에 한 프레임동안 무기가 기본 Idle 위치에 나타난다.

 

해결하는 방법은 여러 방법이 있겠지만, 라이라 스타터 게임에서 애니메이션 블루프린트 링크를 사용하는걸 본 기억이 나서 이걸 사용해보기로 했다. 

 

 

 


 

 

 

애니메이션 블루프린트 링크는 인터페이스를 사용하는것과 비슷하다.

애니메이션의 인터페이스 역할을 하는 애니메이션 레이어 인터페이스가 존재한다.

 

애니메이션 레이어 인터페이스

애니메이션 레이어 인터페이스에는 여러개의 레이어를 만들 수 있다.

 

레이어 예시

이 레이어를 사용하는 클래스에서는 노드를 통해 레이어에서 포즈를 받을 수 있고, 이 레이어를 구현하는 클래스에서는 그래프를 작성해서 포즈를 출력하면 된다.

사용하는 클래스, 구현하는 클래스 모두 클래스 세팅에서 동일한 애니메이션 레이어 인터페이스를 상속받아야한다.

그러면 블루프린트에 사용 가능한 레이어가 표시된다.

 

상속받은 뒤에 나타난 애니메이션 레이어

이걸 드래그해서 사용하거나 더블클릭으로 열어서 구현하면 된다.

 

애니메이션 레이어 인터페이스에서는 레이어의 입력을 설정할 수도 있다.

 

레이어 입력 설정

입력을 추가하면 기본적으로 포즈가 하나 추가되고, 배열을 통해 원하는 값을 받게 설정할 수 있다.

이 레이어를 사용할 때에는 기본적으로 프로퍼티가 노출되지 않는데, 노드의 디테일에서 핀으로 노출시키거나 원하는 값을 바로 연결하면 된다.

 

레이어를 사용할때 프로퍼티를 노출시키는 방법

 


 

레이어를 사용하는 메인 클래스는 하나만 만들어서 기본 애님 클래스로 설정하고, 레이어를 구현하는 클래스는 여러개를 생성해서 런타임에 원하는 클래스를 링크하면 각 클래스의 구현 방식에 따라 애니메이션이 나오게 된다.

 

LinkAnimClassLayers 노드. C++ 함수명도 같다.

새로운 레이어로 교체하더라도 메인 애님 클래스는 계속 작동중이므로 포즈가 초기화되지 않으니 위에서 언급한 문제를 해결할 수 있다.

물론 레이어마다 각자의 변수를 사용한다면 이 값은 초기 상태로 시작한다.

 


 

링크된 레이어는 클래스의 인스턴스라서 레이어마다 각자의 변수를 업데이트하고 사용할 수 있다.

애님 클래스를 링크하면 하나의 레이어에 하나의 인스턴스가 생성된다.

예를들어 메인 애님 클래스에서 3개의 레이어를 사용한다면 링크된 클래스의 인스턴스가 3개 생성되어 각 레이어를 담당하게 된다.

만약 수많은 레이어를 사용한다면 최적화 문제가 생길 수 있으므로, Blueprint Update Animation 이벤트 대신에 Blueprint Thread Safe Update Animation을 사용하는걸 권장한다.

애님 인스턴스 클래스의 Blueprint Thread Safe Update Animation 함수는 별도의 스레드를 통해 상태를 업데이트하며, 블루프린트의 함수 오버라이드를 통해 구현할 수 있다.

 

 

Blueprint Thread Safe Update Animation은 스레드 세이프 함수이므로 상태를 업데이트하기위해 오브젝트나 액터 등에 접근할때 직접 접근하는 방식을 사용할 수 없다.

대신에 Property Access 노드를 사용해야한다.

 

Property Access 노드

프로퍼티 엑세스 노드에 변수나 함수를 바인딩하면 변수의 값과 함수의 리턴값에 접근할 수 있으므로 이 방법으로 상태를 업데이트하면 된다.

 


 

애니메이션 블루프린트 링크의 장점은 레이어의 로직을 원하는대로 구현할 수 있다는 점이다.

자손 블루프린트를 만드는건 에셋만 오버라이드할 수 있지만, 레이어를 구현할 때에는 스테이트 머신은 물론이고 상태 업데이트도 별도로 처리하므로 구조를 잘 설계한다면 모든 상황을 다룰수 있을듯하다.

 

 

 


 

 

 

이 프로젝트에는 일단 Idle과 Locomotion 레이어를 추가했다.

Locomotion의 경우 모두 애디디브 애니메이션이 사용되므로 입력 포즈로 Idle의 출력을 받는다.

 

절차적 애니메이션을 위해 무기의 흔들림을 추가했고, 손은 무기에 붙어서 움직여야하기 때문에 손에 IK도 추가했다.

이건 모든 무기의 공통된 작업이므로 메인으로 사용할 애님 인스턴스 클래스에서 무기의 흔들림과 IK를 다룬다.

레이어에서는 캐릭터의 상태에 맞는 포즈만 출력해주면 된다.

 

레이어의 구현은 별도의 클래스에서 인터페이스를 상속받아서 구현한다.

Idle 레이어 예시

이렇게 구현된 클래스를 부모 클래스로 해서 각 무기마다 자손 클래스를 생성하고, 에셋 오버라이드로 애니메이션을 지정해주면 된다.

 


 

여기에서 각 무기마다 무기의 흔들림 값을 다르게 설정하고 싶었는데, 각 무기의 애님 클래스는 레이어의 애니메이션을 오버라이드하는것만 가능하다는 문제가 있었다.

이와 비슷한 문제가 라이라 스타터 게임에도 있을것으로 보여서 찾아봤더니 링크된 애님 인스턴스에서 메인 애님 인스턴스에 접근하는 방법을 사용하고 있었다.

간단하게 OwningComponent인 스텔레탈 메시를 받아와서 GetAnimInstance를 통해 애님 인스턴스를 받아오고, 클래스에 맞게 캐스팅 하면 된다.

 

따라서 메인 애님 클래스에 무기 흔들림에 사용되는 변수를 구조체로 만들어서 한번에 관리하고, 링크되는 클래스에도 해당 구조체를 추가해서 각 무기마다 다르게 설정한다.

그리고 링크되는 애님 인스턴스가 생성되면 메인 애님 인스턴스에 접근해서 해당 값을 설정하게 했다.

 

레이어 인스턴스가 생성되는 순간에는 OwningComponent가 nullptr이어서 메인 애님 인스턴스에 접근할 수 없다.

따라서 Blueprint Update Animation에서 계속 접근을 시도하다가 Valid한 값을 리턴하면 그때 설정하게 했다.

현재 방식은 일단은 작동하는 방식으로, 나중에 더 좋은 방법을 찾아야할듯하다.

 


 

레이어를 구현한 애님 클래스의 자손을 각 무기마다 만들어서 애니메이션과 변수를 설정했다.

 

 

적용한 결과는 이렇다.

 

 

아래 영상의 10초에서 20초까지 보면 애님 클래스마다 다른 값을 설정할 수 있는것도 볼 수 있다.

https://youtu.be/tS5kNlUM-Sw?si=5_iHkF6X_ecOO-tm&t=10