언리얼 엔진에서 시그니처를 통한 델리게이트의 구현은 3가지 구조로 구성되는데 델리게이트를 선언할 게임모드(리스너/트리거에서 델리게이트에 접근용), 함수를 바인딩할 리스너(스위치에 영향받을 전구), 그리고 해당 델리게이트를 호출할 트리거(스위치 역할)로 구성된다.
리스너 : 이벤트로 인해 영향받을 대상
트리거(핸들러) : 이벤트를 발생시키는 요소(핸들러의 경우 델리게이트 선언까지 포함된 개념)
DECLARE_DELEGATE(FStandardDelegateSignature, <parm>) // 델리게이트 시그니처를 선언
FStandardDelegateSignature MyStandardDelegate; // 시그니처로 사용할 델리게이트 선언
델리게이트는 주로 게임모드에서 선언된다. 그래서 델리게이트를 사용할 리스너 및 트리거는 반드시 게임모드를 include 해줘야 한다.
::BeginPlay()
{
MyGameMode->DelegateSignature.BindUObject(this, 바인딩할 함수)
}
델리게이트 리스너는 델리게이트에 바인딩할 함수 및 바인딩 작업을 구현하는 코드다.
::BeginPlay()에서 리스너는 GetWorld()를 통해 GameMode를 불러오고 GameMode에 선언된 델리게이트를 불러와서 BindUObject()를 호출하여 사용할 함수를 바인딩해준다.
트리거는 이제 델리게이트를 사용하는 액터 클래스이며, 어느 조건에서 델리게이트를 호출할지를 정한다. 주로 Tick 별 조건문보단 액터에서 제공되는 노티파이 함수 등을 사용한다.
트리거는 주로 액터 클래스를 기반으로 작성되며 예를 들어 반응형 이벤트를 구성할 땐 NotifyActorOverlap(AActor) 함수가 호출될 때 델리게이트를 호출시킨다. 이 함수는 액터가 다른 액터(AActor)와 콜리전을 통해 겹쳐질 때 해당 함수가 자동으로 호출되며 겹쳐진 액터가 다시 떨어질 때도 EndOverlap(AActor) 함수가 호출된다.
void AMyTriggerVolume::NotifyActorBeginOverlap(AActor* OtherActor) {
AGameModeBase* GameMode = UGameplayStatics::GetGameMode(GetWorld());
AFPShooterGameMode* MyGameMode = Cast<AFPShooterGameMode>(GameMode);
MyGameMode->MyStandardDelegate.ExecuteIfBound(); } // 바인딩 된 함수를 실행
일단 트리거.cpp에서도 델리게이트에 접근하니 GameMode를 include 되어야 한다.
액터는 다른 액터와 충돌이 일어날 경우 자체적인 NotifyActorBeginOverlap()가 호출되는데 해당 함수를 오버라이드하여 재정의해준다. 맵의 GameMode를 가져오기 위해 GetWorld()를 인자로 GetGameMode()를 호출하여 GameMode를 가져오고 가져온 GameMode를 캐스팅하여 델리게이트 시그니처가 있는 AFPShooterGameMode를 확보한다.
AFPShooterGameMode 포인터로 델리게이트 MyStandardDelegate에 접근하여 델리게이트에 바인딩한 함수를 호출하는 ExecuteIfBound() 함수를 호출하여 콜백 함수를 구현했다.
리스너.cpp에서 MyGameMode->MyStandardDelegate.Unbind();를 호출하면 델리게이트에 바인딩 된 함수를 모두 해제한다. 이를 통해 리스너(액터)가 게임을 떠날 때 게임모드의 MyStandardDelegate 시그니처(포인터)가 댕글링 포인터가 되지 않도록 예방할 수 있다.
일반 델리게이트(Delegate)는 C++에서만 사용할 수 있다.
DECLARE_DELEGATE() // 싱글 (가장 최근에 BindUObject 된 함수 하나만 실행한다)
DECLARE_MULTICAST_DELEGATE() // 멀티 (복수의 함수를 바인딩할 수 있다)
하지만 다이나믹 델리게이트는 직렬화가 되어 블루프린트에서도 사용할 수 있다.
DECLARE_DYNAMIC_DELEGATE() // 싱글
DECLARE_MULTICAST_DYNAMIC_DELEGATE() // 멀티
다이나믹 델리게이트는 바인딩할 함수가 반드시 UNFUNCTION() 메크로가 들어가야 한다.
그리고 블프에서 사용하기 위한 직렬화로 인하여 동작이 좀 더 느리다.
이벤트는 멀티캐스트 델리게이트와 매우 유사하다. 이벤트는 다양한 함수를 바인딩할 수 있지만, 이벤트를 직접 선언한 클래스만 이벤트의 핵심 Broadcast, Clear 함수를 호출할 수 있다. 즉 아무 클래스에서 GetWorld()를 통해 외부 클래스(GameMode)의 델리게이트를 받아서 델리게이트를 호출하는 시도를 사전에 방지할 수 있다. 민감한 함수에 대한 호출 접근권을 제한하기 위해 사용된다.
DECLARE_EVENT(AMyTriggerVolume, EPlayerEntered)
EPlayerEntered OnPlayerEntered;
트리거에 이벤트를 선언하며 트리거에서 바로 이벤트가 브로드캐스팅 된다.
이벤트 매크로 함수다. 매크로 함수의 파라미터는 이벤트를 브로드캐스팅할 클래스, 이벤트 선언을 위한 함수의 시그니처로 파라미터가 구성된다.
NotifyActorBeginOverlap(AActor) {
OnPlayerEntered.Broadcast();
}
큰 차이점으로 노티파이 함수에서 기존처럼 게임모드(외부클래스)를 가져오지 않고 트리거 클래스에 선언된 이벤트를 직접 브로드캐스팅 한다.
UPROPERTY(EditAnywhere)
class AMyTriggerVolume* TriggerEventSource;
리스너에는 이벤트에 등록할 함수를 바인딩한다. 따라서 이벤트가 위치한 클래스를 추가로 선언해줘야 한다.
이벤트의 대상이 되는 액터(조명) 클래스를 바인딩하기 위해 이벤트가 선언된 클래스를 선언해준다. 추가로 해당 클래스의 인스턴스화(액터지정)은 에디터에서 설정한다.
BeginPlay() {
TriggerEventSource->OnPlayerEntered.AddUObject();
}
액터의 BeginPlay() 함수에서 이벤트에 등록할 함수를 바인딩해준다.