0. 개요

어빌리티를 습득하기 위한 조건으로 레벨 요구 사항을 지정한다.

 

 

1. 레벨 요구 사항(Level Requirement)

(1) 어빌리티 정보(Ability Info) 데이터 에셋

Ability Info 구조체에 레벨 요구 사항을 담도록 수정한다.

	// 레벨 요구량
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	int32 LevelRequirement;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSubclassOf<UGameplayAbility> Ability;
};

또한 게임플레이 어빌리티 클래스를 지정할 수 있도록하여 해당 요구량을 어빌리티로 넘기게 할 것이다.

 

 

(2) AuraGameModeBase에서 어빌리티 정보 관리

이제 서버에서 구동되는 게임모드 내에서 어빌리티 정보를 관리하게 된다.

class UCharacterClassInfo;
class UAbilityInfo;

UCLASS()
class AURA_API AAuraGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

public:
	UPROPERTY(EditDefaultsOnly, Category="Character Class Default")
	TObjectPtr<UCharacterClassInfo> CharacterClassInfo;

	UPROPERTY(EditDefaultsOnly, Category="Ability Info")
	TObjectPtr<UAbilityInfo> AbilityInfo;
};

 

 

(3) AuraAbilitySystemLibrary에서 Getter 함수 선언

	UFUNCTION(BlueprintCallable, category = "AuraAbilitySystemLibrary|AbilityInfo")
	static UAbilityInfo* GetAbilityInfo(const UObject* WorldContextObject);
UAbilityInfo* UAuraAbilitySystemLibrary::GetAbilityInfo(const UObject* WorldContextObject)
{
	AAuraGameModeBase* AuraGameMode = Cast<AAuraGameModeBase>(UGameplayStatics::GetGameMode(WorldContextObject));
	if (AuraGameMode == nullptr)
		return nullptr;

	return AuraGameMode->AbilityInfo;
}

 

 

(4) DA_AbilityInfo에서 어빌리티의 레벨 요구량 설정

 

 

(5) BP_AuraGameMode에서 Ability Info 데이터 에셋 클래스 지정

 

 

2. 전기 타입 게임플레이 어빌리티 생성

이제 레벨 요구량에 도달하면 어빌리티를 해금할 수 있도록 표시하기 위해 새로운 스펠인 게임플레이 어빌리티를 생성한다.

이 게임플레이 어빌리티는 테스트를 위해 임시적으로 로그를 표시한다.

 

(1) GA_Electrocute 블루프린트 이벤트 그래프

AuraGameplayAbility를 상속받는 게임플레이 어빌리티 블루프린트를 생성한다.

단순히 로그를 표시한다.

 

(2) Native 게임플레이 태그 추가

	// 어빌리티
	FGameplayTag Abilities_Attack;
	FGameplayTag Abilities_Summon;
	FGameplayTag Abilities_Fire_FireBolt;
	FGameplayTag Abilities_Lightning_Electrocute;
	GameplayTags.Abilities_Lightning_Electrocute = UGameplayTagsManager::Get().AddNativeGameplayTag(
		FName("Abilities.Lightning.Electrocute"),
		FString("Electrocute Ability")
	);

 

 

(3) 클래스 디폴트 수정

방금 추가한 게임플레이 태그를 지정한다.

 

 

(4) DA_AbilityInfo에서 어빌리티 정보 추가하기

 

 

(6) WBP_OffensiveSpellTree에서 글로브에 어빌리티 태그 설정하기

 

 

3. 어빌리티 상태(Ability Status) 변경

게임플레이 어빌리티에 어빌리티 태그가 설정되었고 스펠 트리에도 어빌리티 태그가 설정되었다.

캐릭터가 레벨업을 함에 따라 어빌리티의 상태를 변경하도록 설정할 것이다.

 

(1) AuraAbilitySystemComponent에서 게임플레이 어빌리티 스펙을 태그로 가져오는 함수 선언

활성화된 게임플레이 어빌리티를 순회하여 스펙을 태그로 찾아오는 함수이다.

	FGameplayAbilitySpec* GetSpecFromAbilityTag(const FGameplayTag& AbilityTag);
FGameplayAbilitySpec* UAuraAbilitySystemComponent::GetSpecFromAbilityTag(const FGameplayTag& AbilityTag)
{
    // 활성화된 어빌리티 잠금
    FScopedAbilityListLock ActiveScopeLock(*this);
    for (FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities())
    {
        for (FGameplayTag Tag : AbilitySpec.Ability.Get()->AbilityTags)
        {
            if (Tag.MatchesTag(AbilityTag))
            {
                return &AbilitySpec;
            }
        }
    }
    return nullptr;
}

 

 

(2) AuraAbilitySystemComponent에서 어빌리티 상태를 변경하는 함수 선언

	void UpdateAbilityStatus(int32 Level);
void UAuraAbilitySystemComponent::UpdateAbilityStatus(int32 Level)
{
    UAbilityInfo* AbilityInfo = UAuraAbilitySystemLibrary::GetAbilityInfo(GetAvatarActor());
    for (const FAuraAbilityInfo& Info : AbilityInfo->AbilityInformation)
    {
        if (Info.AbilityTag.IsValid() == false)
            continue;

        // 레벨 요구량보다 레벨이 낮으면 넘김
        if (Level < Info.LevelRequirement)
            continue;

        // 활성화된 어빌리티 중 스펙을 찾지 못하였다면 생성 후 어빌리티 부여
        if (GetSpecFromAbilityTag(Info.AbilityTag) == nullptr)
        {            
            FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(Info.Ability, 1);

            // 해금 가능 태그 부여
            AbilitySpec.DynamicAbilityTags.AddTag(FAuraGameplayTags::Get().Abilities_Status_Eligible);
                        
            GiveAbility(AbilitySpec);

            // 즉시 복제
            MarkAbilitySpecDirty(AbilitySpec);
        }
    }
}

캐릭터의 레벨과 어빌리티 정보의 레벨 요구량을 비교하여 더 높으면 어빌리티 스펙을 생성하여 태그를 수정하고 캐릭터에게 어빌리티를 부여한다.


- AbilitySpec이 nullptr일 때 수행하는 이유

AbilitySpec이 nullptr일 때는 이미 활성화된 어빌리티 목록에 해당 어빌리티가 없다는 뜻이다.

(아직 캐릭터가 습득하지 못한 어빌리티)

따라서 캐릭터의 레벨이 레벨 요구량을 넘게 되면 어빌리티를 획득 가능하도록 수정하고 부여하게 된다.

 

- MarkAbilitySpecDirty 함수

해당 어빌리티 스펙의 더티 플래그를 사용하여 함수 호출 시 브로드캐스팅이 발생하게 한다.


 

(3) AuraCharacter에서 캐릭터가 레벨업 할 때 어빌리티 상태 변경하기

void AAuraCharacter::AddToPlayerLevel_Implementation(int32 InLevel)
{
    AAuraPlayerState* AuraPlayerState = GetPlayerState<AAuraPlayerState>();
    check(AuraPlayerState);
    AuraPlayerState->AddToLevel(InLevel);

    if (UAuraAbilitySystemComponent* AuraASC = Cast<UAuraAbilitySystemComponent>(GetAbilitySystemComponent()))
    {
        AuraASC->UpdateAbilityStatus(AuraPlayerState->GetCharacterLevel());
    }
}

 

 

4. 어빌리티 상태를 스펠 메뉴 위젯에 반영하기

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

어빌리티 상태가 변경되면 위젯에 반영되도록 한다.

DECLARE_MULTICAST_DELEGATE_TwoParams(FAbilityStatusChanged, const FGameplayTag& /*어빌리티 태그*/, const FGameplayTag& /*어빌리티 상태 태그*/);
	FAbilityStatusChanged AbilityStatusChanged;

 

 

(2) 클라이언트 RPC 함수

어빌리티 상태 변경은 서버에서만 발생하기 때문에 클라이언트로 어빌리티 상태가 변경되었음을 알리도록 클라이언트 RPC 함수를 만든다.

	UFUNCTION(Client, Reliable)
	void ClientUpdateAbilityStatus(const FGameplayTag& AbilityTag, const FGameplayTag& StatusTag);
};
void UAuraAbilitySystemComponent::ClientUpdateAbilityStatus_Implementation(const FGameplayTag& AbilityTag, const FGameplayTag& StatusTag)
{
    AbilityStatusChanged.Broadcast(AbilityTag, StatusTag);
}

 

 

(2-1) 클라이언트 RPC 함수 호출

void UAuraAbilitySystemComponent::UpdateAbilityStatus(int32 Level)
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // 활성화된 어빌리티 중 스펙을 찾지 못하였다면 생성 후 어빌리티 부여
        if (GetSpecFromAbilityTag(Info.AbilityTag) == nullptr)
        {            
            FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(Info.Ability, 1);

            // 해금 가능 태그 부여
            AbilitySpec.DynamicAbilityTags.AddTag(FAuraGameplayTags::Get().Abilities_Status_Eligible);
                        
            GiveAbility(AbilitySpec);

            // 즉시 복제
            MarkAbilitySpecDirty(AbilitySpec);
            ClientUpdateAbilityStatus(Info.AbilityTag, FAuraGameplayTags::Get().Abilities_Status_Eligible);
        }
    }
}

 

 

(3) 스펠 메뉴 위젯 컨트롤러에서 어빌리티 상태 변화 델리게이트 바인딩

void USpellMenuWidgetController::BindCallbacksToDependencies()
{
	// 어빌리티 상태 변화 델리게이트 바인딩
	GetAuraASC()->AbilityStatusChaged.AddLambda([this](const FGameplayTag& AbilityTag, const FGameplayTag& StatusTag) 
		{
			if (AbilityInfo)
			{
				FAuraAbilityInfo Info = AbilityInfo->FindAbilityInfoForTag(AbilityTag);
				Info.StatusTag = StatusTag;
				AbilityInfoDelegate.Broadcast(Info);
			}
		});
}

어빌리티 상태 변화 델리게이트에서 받은 매개변수 둘을 사용하여 상태 태그를 변화시키고 어빌리티 정보를 변경한다.

 

 

이제 캐릭터가 레벨업함에 따라 글로브가 변화하는 것을 볼 수 있다.