* 프로그래밍 용어인 델리게이트는 넓은 의미로 본다면 특정 객체가
해야 할 로직을 다른 객체가 대신 처리할 수 있도록 만드는 보편적인 설계의 개념을 의미한다.
* 언리얼 엔진의 델리게이트는 A 객체가 B객체에 작업 명령을 내릴 때,
B객체에 자신을 등록하고 B의 작업이 끝나면, 이 떄 A에게 알려주는 설계방식을 의미한다.
* A가 B가 요구하는 형식으로 멤버 함수를 만들면 이를 B에 등록 할 수 있고,
B가 특정 상황이 될떄, B는 미리 등록해둔 A의 멤버 함수를 호출해주는 방식으로 동작한다.
* C++ 언어 이후 제작된 C# 과 같은 언어는 이러한 델리게이트 시스템을 기본으로 제공한다. 오래된 언어인 C++ 에선 지원하지 않기에, 언리얼 엔진에서 델리게이트를 사용하려면 별도로 구축한 델리게이트 프레임워크를 사용해야 한다.
* 애님 인스턴스에는 애니메이션 몽타주 재생이 끝나면 발동하는 OnMontageEnded라는 델리게이트를 제공한다. 어떤 언리얼 오브젝트라도 UAnimMontage * 인자와 bool 인자를 가진 멤버 함수를 가지고 있다면, 이를 OnMontageEnded 델리게이트에 등록해 몽타주 재생이 끝나는 타이밍을 파악 할수 있다.
* ABCharacter 액터에 위의 함수 형식을 선언하고, 윗줄에 UFUNCTION이라는 매크로를 추가로 선언한다.
OnMontageEnded 델리게이트는 블루프린트의 함수와도 연동할 수 있도록 설계돼 있으므로, C++에서 연동하려는 함수는 블루프린트와 호환되는 함수형으로 선언해야 하기 떄문이다.
해당 인자를 가지는 함수를 ABCharacter 클래스에 선언하고, PostInitializeComponents에서 해당 함수를 애님 인스턴스의 OnMontageEnded 델리게이트에 바인딩 한다. 그리고 현재 공격 중인지 아닌지를 파악 할 수 있도록 bool 변수를 추가로 선언한다.
※ 언리얼 에서 델리게이트는 C++ 객체에만 사용할 수 있는 델리게이트와 C++, 블루프린트 객체가 모두 사용할 수 있는 델리게이트로 나뉜다. 블루프린트 오브젝트는 멤버 함수에 대한 정보를 저장하고 로딩하는 직렬화 매커니즘이 들어있기 때문에 일반 C++ 언어가 관리하는 방법으로 멤버 함수를 관리 할수 없다.
※ 그래서 블루프린트와 관련된 C++함수는 모두 UFUNCTION 매크로를 사용해야 한다.
이렇게 블루프린트 객체와도 연동하는 델리게이트를 언리얼 엔진에서는 다이내믹 델리게이트라고 한다.
다이나믹 딜리게이트는 함수포인터가 아닌, 함수의 이름을 기반으로 등록해 호출하는 방식
C++ 함수 뿐만 아니라 블루프린트 함수도 연결할 수 있게 하기 위해서입니다.
//ABCharacter.h
{
public:
virtual void PostInitializeComponents() override;
private:
UFUNCTION()
void OnAttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
private:
UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = Attack, Meta = (AllowPrivateAccess = true))
bool IsAttacking;
}
* ABCHECK라는 매크로를 추가해 런타임에서 문제가 발생할 떄 붉은색으로 에러로그를 뿌리고 바로 함수를 반환하도록 기능을 추가한다.
//ArenaBattle.h
#define ABCHECK(Expr, ...) {if(!(Expr)) {ABLOG(Error, TEXT("ASSERTION : %s"), TEXT("'"#Expr"'")); return __VA_ARGS__;}}
* 매크로를 활용해 애님 인스턴스의 OnMontageEnded 델리게이트와 선언한 OnAttackMontageEnded를 연결해, 델리게이트가 발동할 때까지 애니메이션 시스템에 몽타주 재생 명령을 내리지 못하게 폰 로직에서 막아준다.
//ABCharacter.cpp
ABCharacter::ABCharacter()
{
IsAttacking = false;
}
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(nullptr == AnimInstance) return;
AnimInstane->PlayAttackMontage();
ABAnim->PlayAttackMontage();
IsAttacking = true;
}
void AABCharacter::OnAttackMontageEnded(UAnimMontage * Montage, bool bInterrupted)
{
ABCHECK(IsAttacking);
IsAttacking = false;
}
※ OnMontageEnded 델리게이트는 블루프린트와 호환되는 성질 외에도 여러 개의 함수를 받을 수 있어서 행동이 끝나면 등록된 모든 함수들에게 모두 알려주는 기능도 제공한다. 이러한 델리게이트르르 멀티캐스트 델리게이트 라고한다
블루프린트 사용가능 + 여러개 함수가능
※ 애님 인스턴스 헤더에 선언된 OnMontageEnded가 사용하는 델리게이트를 저으의한 코드는 다음과 같다. 언리얼 엔진에서 델리게이트의 선언은 언리얼이 제공하는 매크로를 통해 정의되며, 이렇게 정의된 델리게이트의 형식을 시그니처라고 한다.
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams( FOnMontageEndedMCDelegate, UAnimMontage*,
Montage,bool,bInterrupted);
※ 이렇게 두 가지 기능이 있는 OnMontageEnded 델리게이트는 다이내믹 멀티캐스트 델리게이트라고 할 수 있다.
참고로 다이내믹 멀티캐스트 델리게이트에서 사용하는 AddDynamic 함수는 코딩할 떄 비주얼 C++ 인텔리센스에서 검색되지 않는다. 인텔리센스를 무시하고 타이핑해도 컴파일에는 문제가 없다
C++ 함수 뿐만 아니라 블루프린트 함수도 연결 + 블루프린트에서 사용할 수 있게
* 앞으로 캐릭터 클래스에서 애님 인스턴스를 자주 사용할 예정이므로, 이를 멤버 변수로 선언해 런타임에서 이를 활용하도록 구조를 변경한다.
* UABAnimInstance 클래스의 멤버 변수를 선언할 떄 이를 전방 선언으로 설계하는 것이 바람직하다. 전방 선언은 헤더파일에서 같은 모듈에 있는 다른 헤더 파일을 참조하지 않아도 되므로 상호참조를 방지하는 한편, 코드 구조를 관리하기도 좀 더 용이 해진다.
* 이제 폰 로직에서 입력이 들어오면 애님 인스턴스의 PlayAttack을 호출하도록 로직을 추가한다.
//ABCharacter.h
{
private:
UPROPERTY()
class UABAnimInstance* ABAnim;
}
//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;
ABAnim->PlayAttackMontage();
IsAttacking = true;
}
* 델리게이트에 의해 공격의 시작과 종료가 감지되므로, AnimInstance에서 사용한 Montage_IsPlaying 함수는 사용하지 않아도 무방하다.
//ABAnimInstance.cpp
void UABAnimInstance::PlayAttackMontage()
{
Montage_Play(AttackMontage, 1.0f);
}
'Unreal > Game 1 (C++)' 카테고리의 다른 글
12.충돌 설정과 대미지 전달 (PART 1) (0) | 2019.05.02 |
---|---|
11.애니메이션 노티파이 + 콤보 공격 구현 (0) | 2019.05.02 |
9.애니메이션 몽타주 (1) | 2019.05.02 |
8.점프구현 / 캐릭터 상태체크 (0) | 2019.05.02 |
7.애니메이션 폰과 데이터 연동 (0) | 2019.05.02 |