-
[UE5] 단일 캐스팅 델리게이트를 이용한 다중 동작 이벤트 처리UE5 2024. 12. 3. 18:54
1. 단일 캐스팅(Single-cast Delegate) vs 멀티 캐스팅(Multi-cast Delegate) 델리게이트
특징 단일 캐스팅 델리게이트 멀티캐스팅 델리게이트 핸들러 수 한 번에 하나의 함수만 바인딩 가능 여러 함수를 바인딩 가능 사용 사례 특정 이벤트에 하나의 동작만 필요할 때 하나의 이벤트로 여러 동작을 처리할 때 유연성 제한적 높은 유연성 실행 함수 Execute, ExecuteIfBound BroadCast 2. 단일 캐스팅 델리게이트의 다중 동작
- FonTakeItemDelegate 선언 및 델리게이트를 담아줄 구조체 선언
DECLARE_DELEGATE_OneParam(FOnTakeItemDelegate, class UABItemData* /*InItemData*/); USTRUCT(BlueprintType) struct FTakeItemDelegateWrapper { GENERATED_BODY() FTakeItemDelegateWrapper(){} FTakeItemDelegateWrapper(const FOnTakeItemDelegate& InItemDelegate) : ItemDelegate(InItemDelegate) {} FOnTakeItemDelegate ItemDelegate; };
// FTakeItemDelegateWrapper를 담는 리스트 생성 UPROPERTY() TArray<FTakeItemDelegateWrapper> TakeItemActions;
래퍼를 사용하는 이유?
- 단일 캐스팅 델리게이트를 감싸고, 델리게이트 외에도 추가적인 데이터를 저장하거나 관리
- 구조화된 관리, 확장성, 유연성 제공
- 델리게이트와 추가 데이터를 함께 관리할 수 있어 복잡한 로직 처리에 적합.
- 우선순위 처리, 실행 조건 관리, 메타데이터 저장 등에 사용 가능
// 단일 캐스팅 델리게이트를 리스트로 관리 TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &AABCharacterBase::EquipWeapon))); // 무기 장착 TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &AABCharacterBase::DrinkPotion))); // 물약 사용 TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &AABCharacterBase::ReadScroll))); // 스크롤 읽기
// 조건에 따라 단일 캐스팅 델리게이트 리스트에서 적합한 델리게이트만 실행 void AABCharacterBase::TakeItem(UABItemData* InItemData) { if (InItemData) { TakeItemActions[(uint8)InItemData->Type].ItemDelegate.ExecuteIfBound(InItemData); } }
왜 필요한가?
- 멀티캐스트 델리게이트 사용시 하나의 이벤트에 모든 동작이 묶이게 되어 동작 분리가 어려워짐
- 각 동작을 독립적으로 관리 가능하며 특정 동작만 제거하거나 추가하기 쉬움
- 리스트를 통해 각 동작의 실행 순서를 명확히 제어 가능
3. 멀티캐스팅 델리게이트로 조건 분기가 가능할까?
- 멀티캐스팅 델리게이트 정의
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTakeItem, UABItemData*, InItemData);
- 델리게이트 추가
class AABCharacterBase : public ACharacter { GENERATED_BODY() public: UPROPERTY(BlueprintAssignable, Category = "Item Actions") FOnTakeItem OnTakeItem; // 멀티캐스팅 델리게이트 };
- 아이템 유형별 핸들러 등록
void AABCharacterBase::BeginPlay() { Super::BeginPlay(); // 무기 장착 OnTakeItem.AddDynamic(this, &AABCharacterBase::EquipWeapon); // 물약 사용 OnTakeItem.AddDynamic(this, &AABCharacterBase::DrinkPotion); // 스크롤 읽기 OnTakeItem.AddDynamic(this, &AABCharacterBase::ReadScroll); }
- 핸들러 구현
- 각 핸들러 내부터에 아이템 유형을 확인하고 조건을 처리
void AABCharacterBase::EquipWeapon(UABItemData* InItemData) { if (InItemData && InItemData->Type == EItemType::Weapon) { UE_LOG(LogTemp, Log, TEXT("Equipping weapon!")); // 무기 장착 로직 } } void AABCharacterBase::DrinkPotion(UABItemData* InItemData) { if (InItemData && InItemData->Type == EItemType::Potion) { UE_LOG(LogTemp, Log, TEXT("Drinking potion!")); // 물약 사용 로직 } } void AABCharacterBase::ReadScroll(UABItemData* InItemData) { if (InItemData && InItemData->Type == EItemType::Scroll) { UE_LOG(LogTemp, Log, TEXT("Reading scroll!")); // 스크롤 읽기 로직 } }
- 아이템 사용 로직
void AABCharacterBase::TakeItem(UABItemData* InItemData) { if (InItemData) { OnTakeItem.Broadcast(InItemData); // 등록된 모든 핸들러 실행 } }
4. 델리게이트는 직렬화가 불가능하다?
- Delegate wrapper를 사용하는 주된 이유 중 하나는 delegate의 직렬화와 관련된 제약 때문
- 즉, 언리얼엔진에서 UPROPERTY화 시킬 수 없음
- 이런 문제를 해결하기 위해 delegate를 감싸는 wrapper 구조체를 사용하면, 구조체를 통해 직렬화를 지원하게 만들어, delegate의 상태를 보다 쉽게 관리할 수 있음
5. 결론
- 멀티캐스팅 델리게이트는 다수의 핸들러를 한 이벤트에 연결해야 하는 경우에 적합
- 멀티캐스팅 델리게이트를 사용하면 모든 핸들러가 호출된 후 조건을 확인하므로 불필요한 호출이 발생할 가능성있음
- 더군다나, 언리얼엔진의 핵심기능인 UPROPERTY화가 불가능함
- 단일 캐스팅 델리게이트를 리스트화하여 조건에 따라 실행 시키는 것이 조건 기반 실행에 적합하고, 확장성과 관리 측면에서 유용
'UE5' 카테고리의 다른 글
[UE5] 네트워크 멀티플레이어 (0) 2024.12.10 [UE5] 추상클래스와 인터페이스 (0) 2024.12.06 [UE5] 순수 가상 함수(Pure Virtual Function) (0) 2024.12.06 [UE5] 인터페이스의 활용 (0) 2024.12.05 [UE5] CoreMinimal.h란? (1) 2024.12.03