1. 델리게이트 선언

(1) AuraAbilitySystemComponent에서 델리게이트 선언

커스텀 어빌리티 시스템 컴포넌트에서 어빌리티를 각 액터에게 부여하고 있다.

새로운 델리게이트를 사용하여 어빌리티가 부여되면 OverlayWidgetController에서 해당 정보를 위젯으로 전달하도록 한다.

DECLARE_MULTICAST_DELEGATE(FAbilitiesGiven);
public:
	// 델리게이트 바인딩
	void AbilityActorInfoSet();

	FEffectAssetTags EffectAssetTags;
	FAbilitiesGiven AbilitiesGivenDelegate;
void UAuraAbilitySystemComponent::AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities)
{
    for (TSubclassOf<UGameplayAbility> AbilityClass : StartupAbilities)
    {
        // 게임플레이 어빌리티 스펙 생성
        FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass, 1);

        if (const UAuraGameplayAbility* AuraAbility = Cast<UAuraGameplayAbility>(AbilitySpec.Ability))
        {
            AbilitySpec.DynamicAbilityTags.AddTag(AuraAbility->StartupInputTag);
            GiveAbility(AbilitySpec);
        }
    }
    AbilitiesGivenDelegate.Broadcast();
}

캐릭터들에게 어빌리티가 부여된 후에 AbilitiesGivenDelegate가 호출된다.

 

 

(2) OverlayWidgetController에서 콜백 함수 바인딩하기

오버레이 위젯 컨트롤러의 BindCallbacksToDependencies 함수에서 델리게이트와 함수를 바인딩한다.

	void OnInitializeStartupAbilities();
void UOverlayWidgetController::BindCallbacksToDependencies()
{
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    if (auto* AuraASC = Cast<UAuraAbilitySystemComponent>(AbilitySystemComponent))
    {
        // 어빌리티 부여 완료
        AuraASC->AbilitiesGivenDelegate.AddUObject(this, &UOverlayWidgetController::OnInitializeStartupAbilities);

        // 플로팅 메세지 생략
        AuraASC->EffectAssetTags.AddLambda(
    }
}

 

 

2. 두 가지 상황

서버에서 어빌리티를 부여하는 시점이 델리게이트가 콜백 함수에 바인드하기 이전 혹은 이후가 될 수 있다.

(델리게이트를 함수에 바인드 하기 전에 호출해버릴 수 있음)

따라서 불리언을 사용하여 두 가지 상황을 모두 처리한다.

 

(1) AuraAbilitySystemComponent에서 불리언 선언

public:
	// 델리게이트 바인딩
	void AbilityActorInfoSet();

	FEffectAssetTags EffectAssetTags;
	FAbilitiesGiven AbilitiesGivenDelegate;

	void AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities);
	bool bStartupAbilitiesGiven = false;
void UAuraAbilitySystemComponent::AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities)
{
    for (TSubclassOf<UGameplayAbility> AbilityClass : StartupAbilities)
    {
        // 게임플레이 어빌리티 스펙 생성
        FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(AbilityClass, 1);

        if (const UAuraGameplayAbility* AuraAbility = Cast<UAuraGameplayAbility>(AbilitySpec.Ability))
        {
            AbilitySpec.DynamicAbilityTags.AddTag(AuraAbility->StartupInputTag);
            GiveAbility(AbilitySpec);
        }
    }
    bStartupAbilitiesGiven = true;
    AbilitiesGivenDelegate.Broadcast();
}

어빌리티가 먼저 부여되고 델리게이트 호출이 나중에 일어나는 상황에서는 콜백 함수를 바인딩하지 않고 즉시 호출한다.

    if (auto* AuraASC = Cast<UAuraAbilitySystemComponent>(AbilitySystemComponent))
    {
        if (AuraASC->bStartupAbilitiesGiven)
        {
            // 콜백 함수 바인드 필요 없이 바로 호출
            UOverlayWidgetController::OnInitializeStartupAbilities();
        }
        else
        {
            // 어빌리티 부여 이전이면 델리게이트에 함수 바인딩
            AuraASC->AbilitiesGivenDelegate.AddUObject(this, &UOverlayWidgetController::OnInitializeStartupAbilities);
        }

 

 

(2) 델리게이트 수정

이제 FAbilitiesGiven 델리게이트를 수정하여 AuraAbilitySystemComponent를 매개변수로 전달하여 사용할 것이다.

 

(2-1) AuraAbilitySystemComponent

DECLARE_MULTICAST_DELEGATE_OneParam(FAbilitiesGiven, UAuraAbilitySystemComponent*);
void UAuraAbilitySystemComponent::AddCharacterAbilities(const TArray<TSubclassOf<UGameplayAbility>>& StartupAbilities)
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    bStartupAbilitiesGiven = true;
    AbilitiesGivenDelegate.Broadcast(this);
}

 

 

(2-2) OverlayWidgetController

	void OnInitializeStartupAbilities(UAuraAbilitySystemComponent* AuraAbilitySystemComponent);

 

 

 

(2-3) 어빌리티를 순회하는 델리게이트 생성

위젯 컨트롤러에서 AuraAbilitySystemComponent를 가져와 루프하는 것이 아니라

AuraAbilitySystemComponent에서 루프하기 위해 델리게이트를 생성한다.

DECLARE_DELEGATE_OneParam(FForEachAbility, const FGameplayAbilitySpec&)
void UAuraAbilitySystemComponent::ForEachAbility(const FForEachAbility& Delegate)
{
    // 어빌리티가 차단되거나 변경되었을 수 있음, 어빌리티 잠금
    FScopedAbilityListLock ActiveScopeLock(*this);

    for (const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
    {
        if (Delegate.ExecuteIfBound(AbilitySpec) == false)
        {
            UE_LOG(LogAura, Error, TEXT("Failed to execute delegate in %hs"), __FUNCTION__);
        }
    }
}

어빌리티가 중간에 태그에 의해 차단(Block)되거나 취소(Canceled)될 수 있으므로 ActiveScopeLock을 이용하여 어빌리티들을 잠근다.

 

 

(2-4) 입력 태그 가져오는 함수

FGameplayTag GetInputTagFromSpec(const FGameplayAbilitySpec& AbilitySpec);
FGameplayTag UAuraAbilitySystemComponent::GetInputTagFromSpec(const FGameplayAbilitySpec& AbilitySpec)
{
    for (FGameplayTag Tag : AbilitySpec.DynamicAbilityTags)
    {
        if (Tag.MatchesTag(FGameplayTag::RequestGameplayTag(FName("InputTag"))))
        {
            return Tag;
        }
    }
    return FGameplayTag();
}

 

 

(2-5) 어빌리티 정보 가져온 후 위젯 블루프린트로 정보 넘기기

void UOverlayWidgetController::OnInitializeStartupAbilities(UAuraAbilitySystemComponent* AuraAbilitySystemComponent)
{
    // TODO : 주어진 어빌리티에 대한 모든 어빌리티 정보를 가져옴, 위젯으로 전달
    
    // 어빌리티가 주어지지 않았다면 리턴
    if (!AuraAbilitySystemComponent->bStartupAbilitiesGiven)
        return;

    // 어빌리티를 순회하지 않고 델리게이트를 사용
    // 콜백 함수 바인딩
    FForEachAbility BroadcastDelegate;
    BroadcastDelegate.BindLambda([this, AuraAbilitySystemComponent](const FGameplayAbilitySpec& AbilitySpec)
        {
            FAuraAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(AuraAbilitySystemComponent->GetAbilityTagFromSpec(AbilitySpec));
            Info.InputTag = AuraAbilitySystemComponent->GetInputTagFromSpec(AbilitySpec);
            
            // 블루프린트 델리게이트
            AbilityInfoDelegate.Broadcast(Info);
        });

    // 델리게이트 실행
    AuraAbilitySystemComponent->ForEachAbility(BroadcastDelegate);
}

 

 

3. 순서 정리

(1) FForEachAbility 델리게이트에 람다 함수 바인딩

 

(2) FForEachAbility 델리게이트 실행

- AuraASC 내의 ForEachAbility 함수 실행.

- 어빌리티 목록 잠금.

- AuraASC 내의 활성 가능한 모든 어빌리티(어빌리티 스펙)에 대해서 순회.

- 전달 받은 델리게이트에 해당 어빌리티 스펙이 바운드 되어 있으면 델리게이트 실행.

 

(3) 람다 함수 실행

- 람다 함수 내에서 델리게이트 실행 시 전달 받은 AbilitySpec을 사용하여 태그와 일치하는 어빌리티 정보를 가져온다.

- 람다 함수 내에서 AbilitySpec을 이용하여 입력 태그를 가져와 어빌리티 정보에 대입한다.

- 위젯 델리게이트로 어빌리티 정보를 넘긴다.