본문 바로가기

언리얼C++게임개발/08.애니메이션 시스템 활용

델리게이트

UPROPERTY()
class UABAnimInstance* ABAnim;

앞서 같은 애니메이션을 중복플레이를 방지하기 위해 플레이중임을 체크했는데 이보다는 애니메이션이 끝나면 자동으로 폰에게 알려주는 편이 더 효과적이다. 언리얼이 제공하는 델리게이션기능을 사용해서 구현할 수 있다.

 델리게이션이란 보편적으로 특정 객체가 해야할 로직을 다른 객체가 대신 처리할 수 있도록 만드는 보편적인 개념이다. A가 B에게 작업명령을 내리고 자신을 등록후 끝나면 A에게 알려주는 설계방식이다.

애님 인스턴스에는 애니메이션 몽타주 재생이 끝나면 발동하는  OnMontageEnded라는 델리게이트를 제공한다.  어떤 언리얼오브젝트라도 UAnimMontage * 인자와 bool 인자를 가진 멤버함수를 가지고 있다면, 이를 OnMontageEnded 델리게이트에 등록해 몽타주 재생이 끝나는 타이밍을 파악할 수 있다.

OnMontageEnded델리게이트는 여러개의 함수를 받을 수 있어서 등록된 모든 함수들에게 멀티캐스트할수 있고 멀티캐스트델리게이트라고 한다. 언리얼에서는 Signature라고 매크로를 통해 정의되어 있다. 

* Delegate for when Montage is completed, whether interrupted or finished
* Weight of this montage is 0.f, so it stops contributing to output pose
*
* bInterrupted = true if it was not property finished
*/
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMontageEndedMCDelegate,
	UAnimMontage*, Montage, bool, bInterrupted);

이렇게 두가지 기능이 있는 걸 다이내믹 멀티캐스트 델리게이트라고 할수 다. 여기서 사용하는 AddDynamic함수는 코딩할 때 비쥬얼 인텔리센스에서 검색되지 않지만 에러는 아니다

 PostInitializeComponents() 에서 해당함수를 애님 이스턴스의 OnMontageEnded 델리게이트에 바인딩한다. 그리고 현재 공격중인지 아닌지를 파악할 수 있도록  bool변수를 추가로 선언한다.

ABCharacter.h

public:	
// Called every frame
virtual void Tick(float DeltaTime) override;
virtual void PostInitializeComponents() override;

private:
UFUNCTION()
void OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
bool IsAttacking;

ABCharacter.cpp

void AABCharacter::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	ABAnim = Cast<UABAnimInstance>(GetMesh()->GetAnimInstance());
	ABCHECK(nullptr != ABAnim);

	ABAnim->OnMontageEnded.AddDynamic(this, &AABCharacter::OnAttackMontageEnded);
    //....
}


void AABCharacter::Attack()
{
	if (IsAttacking) return;
	auto AnimInstance = Cast<UABAnimInstance>(GetMesh()->GetAnimInstance());
    if(!AnimInstance) return;
    AnimInstance->PlayAttackMontage();
    IsAttacking = true;

}

void AABCharacter::OnAttackMontageEnded(UAnimMontage * Montage, bool bInterrupted)
{
	ABCHECK(IsAttacking);
	ABCHECK(CurrentCombo > 0);
	IsAttacking = false;
	//AttackEndComboState();
}

AnimInstance변수를 자주 사용하므로 헤더파일에 포함시키고

ABCharacter.h

UPROPERTY()
class UABAnimInstance* ABAnim;

ABCharacter.cpp의 PostIniializeCompoents()에서 구현한다.

void AABCharacter::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	ABAnim = Cast<UABAnimInstance>(GetMesh()->GetAnimInstance());
	ABCHECK(nullptr != ABAnim);
}
void AABCharacter::Attack()
{
	if (IsAttacking) return;
    Anim->PlayAttackMontage();
    IsAttacking = true;

델리게이트에 의해 공격의 시작과 종료가 감지되므로 AnimInstance에서 사용한 Montage_IsPlaying 함수는 사용하지 않아도 된다. 이를 제거한 코드는 다음과 같다.

void UABAnimInstance::PlayAttackMontage()
{
	Montage_Play(AttackMontage, 1.0f);
}