Anim Instance 란?

애님 인스턴스는 쉽게 생각하면, 애니메이션을 만들기 위해 사용되는 데이터들과 그에 대한 명령어 세트를 모아둔 오브젝트 인스턴스라고 보면 된다.

구조적으로는 그저 하나의 컨테이너이라고 봐도 무방하다. Facade 디자인 패턴을 사용해서 만들어진 오브젝트로써, 현재 실행되고 있는 애니메이션과 애니메이션 몽타주들을 관리하는 오브젝트이다.

애님 인스턴스를 통해 우리는 애니메이션 블루프린트로 짜여진 구조에 명령을 내리거나, 애니메이션, 애니메이션 몽타주를 실행하고, 실행에 따른 여러 옵션들을 설정할 수 있게 된다.

애님 인스턴스는 SkeletalMeshComponent에 숨어있는데, 코드로 애님 인스턴스를 가져오는 방법은 아래와 같다.

먼저, Anim Blueprint가 생성자 이전에는 접근이 불가능한 오브젝트이므로, 생성자에서는 불러오는게 불가능하다. 이 문제를 겪어봐서 잘 안다… ㅠ

http://158.247.209.73/unreal-engine/4-19-%eb%b2%84%ec%a0%84%ec%97%90%ec%84%9c-%ec%95%a0%eb%8b%88%eb%a9%94%ec%9d%b4%ec%85%98-%eb%b8%94%eb%a3%a8%ed%94%84%eb%a6%b0%ed%8a%b8-%ec%84%b8%ed%8c%85-%eb%b0%a9%eb%b2%95/

이니셜라이저가 끝난 뒤인, PostInitializeComponents 에서 사용하는 것이 가장 바람직 한 것 같다.

먼저 애님 인스턴스를 사용할 캐릭터나, 그와 비슷한 객체에 animInstance를 담아둘 포인터를 하나 선언하고, 같이 쓸 PostInitializeComponents 함수를 만들어준다.

UAnimInstance* animInstance;
virtual void PostInitializeComponents() override;

그 후 애님 인스턴스를 SkeletalMeshComponent에서 가져와 사용하는 방법은 이렇다.

void ATESTCharacter::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	animInstance = GetMesh()->GetAnimInstance();
}

정말 쉽게 가져와 사용할 수 있다.

그럼 애님 인스턴스에 무엇을 할 수 있는지 알아봐보자.

공식 문서를 확인해보면 이 오브젝트에서 상당히 많은 것들을 처리할 수 있다는 느낌을 받을 수 있다.

https://api.unrealengine.com/INT/API/Runtime/Engine/Animation/UAnimInstance/index.html

애님 몽타주 실행, 정지, 섹션 건너뛰기 등등과, 커브 설정, 애니메이션 업데이트, 슬롯 애니메이션 실행, 루트모션 모드 조정, 기본적인 초기화부터 업데이트 함수들 등등 애니메이션에 관한 대부분의 작업을 이곳에서 진행하는 것 같다.

애님 인스턴스는 애니메이션 블루프린트와 연동해서 사용한다.

직접 애니메이션 인스턴스와 블루프린트를 만들어보겠다. 먼저 애님 인스턴스다.

C++ 클래스를 추가하기 위해 엔진 컨텐츠 브라우저에서 오른쪽 클릭을 한 후, 새 C++ 클래스를 누른다.

그리고 우상단에 있는 모든 클래스 표시를 누른 뒤 animInstance를 검색한다. 그 후 추가적인 설정은 알아서 하면 된다.

추가 해서 빌드가 잘 되었다면 이제 코드를 추가한다.

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

#pragma once

#include "Engine.h"
#include "Animation/AnimInstance.h"
#include "MyAnimInstance.generated.h"

UCLASS()
class TEST_API UMyAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
	
public:
	UMyAnimInstance();
	
	virtual void NativeBeginPlay() override;
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

	UFUNCTION(BlueprintCallable)
	void Attack();
	
private:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = MyInstance, meta = (AllowPrivateAccess = "true"))
	bool bIsAttack;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyAnimInstance.h"

UMyAnimInstance::UMyAnimInstance()
{
	bIsAttack = false;
}

void UMyAnimInstance::NativeBeginPlay()
{
	Super::NativeBeginPlay();
}

void UMyAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);
}

void UMyAnimInstance::Attack()
{
	//TODO : Play Attack Montage
}

이렇게 bIsAttack을 하나 갖고 있는 기본적인 AnimInstance 설정이 끝났다. 나는 추가적으로 공격을 예제로 하기 위해 Attack 함수를 넣어두었다.

이제 애니메이션 블루프린트를 만들어서 연동해보자.

애니메이션 블루프린트는 C++ 클래스로 뽑을 수 없다. 애니메이터들이 쓰는 툴이겠거니와, C++로 구현하기엔 너무 복잡한 로직이 될 수있고, 비주얼 적인 노드로 구성하는게 훨씬 더 효율적이기 때문이다. 코드상에서는 레퍼런싱만 가능한 것으로 알고있다.

다른 애니메이션을 보여주기 위해 예전에 마켓에서 사둔 짱 멋진 애니메이션 애셋을 다운로드 받아두었다.

애님 블루프린트를 만드는건 상당히 쉽다. 그냥 콘텐츠 브라우저에서 오른쪽 클릭 -> 애니메이션 – > 애니메이션 블루프린트를 누르면 된다.

그럼 이렇게 선택하는 창이 나오는데 여기서 우리가 만들었던 애님 인스턴스를 선택해주자. 그리고 애니메이션에 맞는 스켈레톤을 고르면 완성이다.

만들고 나서 내 블루프린트 탭에 변수창을 보면 우리가 만들었던 bIsAttack bool 형 변수가 보이질 않는다. 이때 우상단에 있는 눈 버튼을 눌러 상속된 변수를 표시해주면 된다.

이렇게 애님 블루프린트를 조합해 여러가지 노드를 편성해 다양한 애니메이션들을 보여줄 수있다.

나는 애님 몽타주만 만들어서 공격만 해보겠다. 그러기 위해서는 일단 AnimGraph 탭을 들어가서 Slot을 검색해 Default Slot을 추가해준다. 그리고 그옆에는 가장 기본 자세인 idle 같은 애니메이션을 아무거나 넣어준다.

이제 준비는 대충 끝났다. 애니메이션 몽타주를 만들자.

애니메이션 블루프린트를 만들때처럼 오른쪽 클릭을 해서 이번엔 애니메이션 몽타주를 누른다. 스켈레톤을 선택해주고 나는 이름을 AttackMontage로 지었다.

애니메이션 몽타주에 대한 설명은 나중에 추가적으로 더 하겠다.

저 화면에서 오른쪽 아래에 있는 애셋 브라우저를 열어 아무런 애니메이션이나 갖다둬보자.

완료됬다면 저장을 누르자. (나는 루트모션 애니메이션이 있어서 루트모션을 켜두었다.)

이제 캐릭터 코드로 가서, 애니메이션 블루프린트와, 애니메이션 인스턴스를 설정해주자. 캐릭터 생성자 코드에 다음과 같이 설정한다.


	//바디 메시 설정
	static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_BODY(TEXT("SkeletalMesh'/Game/Frank_RPG_Dual/Skeletal_Meshes/SK_Mannequin.SK_Mannequin'"));
	if (SK_BODY.Succeeded())
	{
		//캐릭터에서 상속된 메시에 스켈레탈 메시 설정
		GetMesh()->SetSkeletalMesh(SK_BODY.Object);
	}
	//애니메이션 모드 설정
	GetMesh()->SetAnimationMode(EAnimationMode::AnimationBlueprint);

	//애니메이션 블루프린트 가져오기 클래스를 가져오는 것이기 때문에 맨 마지막에 _C를 붙여아함
	static ConstructorHelpers::FClassFinder<UAnimInstance> BLADER_ANIM(
		TEXT("AnimBlueprint'/Game/Frank_RPG_Dual/Animations/AnimBP_Blader.AnimBP_Blader_C'"));
	if (BLADER_ANIM.Succeeded())
	{
		//애니메이션 블루프린트를 설정
		GetMesh()->SetAnimInstanceClass(BLADER_ANIM.Class);
	}

애님 인스턴스는 위에 PostInitializeComponents에서 미리 설정해두었었다.

그리고 이제 몽타주 재생을 위한 설정을 진행해보자.

마우스 왼쪽 키를 누르면 공격애니메이션을 재생하는 간단한 방법은 이렇다.

프로젝트 세팅 – > 입력 -> Action Mappings 에서 + 를 눌러 키 바인딩을 추가하자.

설정을 끝내면 코드로 돌아와 키를 인식하게 해줘야한다. 캐릭터의 함수중 SetupPlayerInputComponent 속에 아래와 같이 설정하기 전에 캐릭터 클래스에 DoAttack 이라는 함수를 미리 추가해 두길 바란다.

PlayerInputComponent->BindAction("MouseLeft", IE_Pressed, this, &ATESTCharacter::DoAttack);

이제 만든 DoAttack 함수는 IE_Pressed로 설정되었기 때문에, 마우스 왼쪽키를 누르면 호출된다.

애니메이션 인스턴스에 몽타주 재생 관련 코드를 넣어보자. 먼저 애니메이션 몽타주를 불러와야한다.

애니메이션 몽타주의 포인터를 만든 후 인스턴스를 불러오자. 헤더에 다음을 추가한다.

UAnimMontage* attackMontage;

그 후 생성자에서 애니메이션 몽타주를 불러온다.

UMyAnimInstance::UMyAnimInstance()
{
	bIsAttack = false;

	ConstructorHelpers::FObjectFinder<UAnimMontage> ATTACK_MONTAGE(TEXT("AnimMontage'/Game/Frank_RPG_Dual/Animations/AttackMontage.AttackMontage'"));
	if (ATTACK_MONTAGE.Succeeded())
	{
		attackMontage = ATTACK_MONTAGE.Object;
	}
}

이제 미리 만들어 두었던 Attack 함수의 구현부로 가서 몽타주를 재생하자.

void UMyAnimInstance::Attack()
{
	Montage_Play(attackMontage);
}

마지막으로 캐릭터의 DoAttack 함수에서 몽타주를 플레이하는 애님인스턴스의 Attack함수를 호출해주자.

void ATESTCharacter::DoAttack()
{
	UMyAnimInstance* myAnimInst = Cast<UMyAnimInstance>(animInstance);
	if (myAnimInst != nullptr)
	{
		myAnimInst->Attack();
	}
}

이제 컴파일 후 확인해보자.

잘 되는 것을 확인했다.

이처럼 애님 인스턴스는 애니메이션의 특수한 제어등을 위해 사용된다. 잘 사용하고 관리하면 locomotion 시스템과 그보다 더한 어려운 로직도 구현이 가능하니 더 깊게 공부해봐야겠다.