#1. 개념
1. 인자 바인딩
Unreal에서 인자가 없는 델리게이트/이벤트에 콜백 함수를 등록할 때, 해당 콜백이 호출될 때 사용될 추가 인자를 미리 지정하는 기능입니다. 이를 통해, 콜백 함수는 델리게이트 선언 시 인자가 없지만, 바인딩 시점에 지정된 인자 값은 델리게이트/이벤트가 호출될 때마다 콜백 함수에 해당 인자가 전달됩니다. 이 기능을 통해 콜백 함수에 추가적인 콘텍스트나 추가 정보를 전달할 수 있습니다.
2. 동작 방식
//@이벤트 선언
DECLARE_MULTICAST_DELEGATE(FSomeDelegate)
class SomeClass
{
public:
FSomeDelegate SomeDelegate;
};
//@콜백 선언
class AnotherClass
{
protected:
UFUNCTION()
void CallbackFunc(EItemType ItemType);
};
//@콜백 등록
void AnotherClass::BindToCallback(EItemType ItemType)
{
//...
SomeClass->SomeDelegate.AddUObject(this, "CallbackFunc", ItemType);
}
//@콜백
void AnotherClass::BindToCallback(EItemType ItemType)
{
//...
}
'인자 바인딩'을 통해 델리게이트에서 정의한 함수 타입에 더해, 인자를 추가할 수 있습니다. 이를 통해, 델리게이트와 다른 함수 시그니처를 갖는 콜백 함수를 델리게이트에 등록 가능합니다.
3. 특징
- 장점
- 유연성 증가: 동일한 함수를 다양한 상황에서 재사용할 수 있습니다. 바인딩 시점에 다른 인자를 전달함으로써 함수의 동작을 상황에 맞게 조정할 수 있습니다.
- 코드 재사용성 향상: 비슷한 기능을 하는 여러 함수를 만들 필요 없이, 하나의 함수를 다양한 인자로 바인딩하여 사용할 수 있습니다.
- 콘텍스트 제공: 콜백 함수에 추가적인 정보나 콘텍스트를 제공할 수 있어, 함수가 더 의미 있는 작업을 수행할 수 있습니다.
- 캡슐화 개선: 객체의 내부 상태를 직접 노출하지 않고도 필요한 정보를 콜백 함수에 전달할 수 있습니다.
- 성능 최적화: 함수 호출 시마다 인자를 계산하거나 찾는 대신, 미리 계산된 값을 사용할 수 있어 성능상 이점이 있을 수 있습니다.
- 단점
- 복잡성 증가: 인자 바인딩을 사용하면 코드의 흐름을 파악하기 어려워질 수 있습니다. 특히 많은 곳에서 사용될 경우 디버깅이 복잡해질 수 있습니다.
- 메모리 사용 증가: 바인딩된 인자들은 델리게이트 객체와 함께 저장되므로, 많은 델리게이트를 사용할 경우 메모리 사용량이 증가할 수 있습니다.
- 타입 안정성 문제: 컴파일 시점에 타입 체크가 이루어지지 않을 수 있어, 런타임 에러의 가능성이 있습니다.
- 생명주기 관리의 어려움: 바인딩된 객체나 값의 생명주기를 적절히 관리하지 않으면 댕글링 포인터 등의 문제가 발생할 수 있습니다.
- 가독성 저하: 인자 바인딩을 과도하게 사용하면 코드의 의도를 파악하기 어려워질 수 있습니다.
- 디버깅의 어려움: 바인딩된 인자의 값을 추적하기 어려울 수 있어, 디버깅 과정이 복잡해질 수 있습니다.
4. 코드
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Engine/World.h"
DECLARE_DELEGATE_OneParam(FOnDamageDelegate, float);
class AMyActor : public AActor
{
GENERATED_BODY()
public:
AMyActor()
{
PrimaryActorTick.bCanEverTick = true;
}
virtual void BeginPlay() override
{
Super::BeginPlay();
// 일반적인 바인딩
OnDamageDelegate.BindUObject(this, &AMyActor::TakeDamage);
// 인자 바인딩을 사용한 바인딩
OnDamageDelegate.BindUObject(this, &AMyActor::TakeDamageWithMultiplier, 2.0f);
// 델리게이트 호출
OnDamageDelegate.ExecuteIfBound(10.0f);
}
void TakeDamage(float DamageAmount)
{
UE_LOG(LogTemp, Warning, TEXT("Took damage: %f"), DamageAmount);
}
void TakeDamageWithMultiplier(float DamageAmount, float Multiplier)
{
float FinalDamage = DamageAmount * Multiplier;
UE_LOG(LogTemp, Warning, TEXT("Took damage with multiplier: %f"), FinalDamage);
}
private:
FOnDamageDelegate OnDamageDelegate;
};
'인자 바인딩'을 활용하여 델리게이트가 정의하는 함수 형식에 없는 추가적인 인자를 등록하여, 이를 활용하여 이벤트 호출을 통해 각 콜백들에서 고유의 동작들을 정의할 수 있습니다.
6. 예제
Inventory Tool Bar는 Inventory UI의 세부 항목을 EItemType을 기준으로 나누어 각 Type별 버튼을 만들어 해당되는 아이템들을 분류합니다. 이때, 각 아이템 유형을 대표하는 버튼이 존재하며, 해당 버튼은 UCustomButton으로 정의되어 있습니다.
Inventory Tool Bar는 콜백 등록 과정에서, 하나의 콜백에 대하여 서로 다른 Item Type을 갖도록 하고, 동일한 이벤트에 이들을 등록합니다. 이를 통해, 버튼 클릭 이벤트 발생 시 'ItemType' 인자를 통해 어떤 버튼에서 클릭 이벤트를 유추할 수 있습니다.
물론, 델리게이트의 타입 안정성을 제공받지 못하고, 동일한 콜백 함수를 Item Type 개수만큼 등록하여 추가적인 메모라 공간이 필요하다는 단점이 있습니다. 다만, Custom Button을 Item Type 별로 분류하여 하위 클래스들을 만드는 작업은 과도한 코드 중복이 우려되었습니다. 추가적으로, Custom Button은 Inventory Tool Bar 외에 수많은 UI들에서 활용하는 일반적인 '버튼'의 기능을 하도록 계획했으며, Custom Button을 이러한 일반적인 상태로 남겨두기 위해 Inventory Tool Bar에서 '인자 바인딩' 작업을 활용했습니다.
'게임개발 > Unreal C++' 카테고리의 다른 글
[Unreal]#TSoftObjectPtr (0) | 2024.11.16 |
---|---|
[Unreal]#FInputMode (2) | 2024.09.08 |
[Unreal]#UI 최적화 (1) | 2024.08.28 |
[Unreal]#UFUNCTION 매크로 (0) | 2024.08.28 |
[Unreal]#비동기 프로그래밍 (0) | 2024.08.21 |