🎮 UBFAbilitySystemComponent.h
UCLASS() class BOSSFIGHT_API UBFAbilitySystemComponent : public UAbilitySystemComponent { GENERATED_BODY() public: void OnAbilityInputPressed(const FGameplayTag& InInputTag); void OnAbilityInputReleased(const FGameplayTag& InInputTag); };🎮 UBFAbilitySystemComponent.cpp
void UBFAbilitySystemComponent::OnAbilityInputPressed(const FGameplayTag& InInputTag) { if (!InInputTag.IsValid()) { return; } for (const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) { if (!AbilitySpec.Ability->AbilityTags.HasTagExact(InInputTag)) { continue; } TryActivateAbility(AbilitySpec.Handle); } } void UBFAbilitySystemComponent::OnAbilityInputReleased(const FGameplayTag& InInputTag) { if (!InInputTag.IsValid() || !InInputTag.MatchesTag(BFGameplayTag::Input_Hold)) { return; } for (const FGameplayAbilitySpec& AbilitySpec : GetActivatableAbilities()) { if (AbilitySpec.DynamicAbilityTags.HasTagExact(InInputTag) && AbilitySpec.IsActive()) { CancelAbilityHandle(AbilitySpec.Handle); } } }우선 가장 기본적인 입력에 따라 Ability를 실행 및 종료 해주는 기능을 구현 해주었다.
또한 Dash기능 처럼 키를 입력 하고 있을때 작동하는 Ability를 위해 BFGameplayTag에 Input_Hold를 추가 해주었고, 해당 Tag에서 파생된 Tag를 가진 Ability일 경우 누르고 있으면 작동을 하고, 키를 떼면 종료 하도록 구현 하였다.
🎮 UBFAttributeSet.h
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \ GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) UCLASS() class BOSSFIGHT_API UBFAttributeSet : public UAttributeSet { GENERATED_BODY() public: UBFAttributeSet(); virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)override; UPROPERTY(BlueprintReadOnly, Category = "Health") FGameplayAttributeData CurrentHP; ATTRIBUTE_ACCESSORS(UBFAttributeSet, CurrentHP) UPROPERTY(BlueprintReadOnly, Category = "Health") FGameplayAttributeData MaxHP; ATTRIBUTE_ACCESSORS(UBFAttributeSet, MaxHP) };🎮 UBFAttributeSetBase.cpp
UBFAttributeSet::UBFAttributeSet() { InitCurrentHP(1.0f); InitMaxHP(1.0f); } void UBFAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) { if (Data.EvaluatedData.Attribute == GetCurrentHPAttribute()) { const float NewCurrentHP = FMath::Clamp(GetCurrentHP(), 0.0f, GetMaxHP()); SetCurrentHP(NewCurrentHP); } if (Data.EvaluatedData.Attribute == GetMaxHPAttribute()) { const float NewMaxHP = FMath::Clamp(GetMaxHP(), 0.0f, 200.0f); SetMaxHP(NewMaxHP); } }간단하게 현재 HP와 최대HP를 추가하고, PostGameplayEffectExecute()를 구현 하였다.
🎮 ABFBaseCharacter.h
class UBFAbilitySystemComponent; class UBFAttributeSetBase; UCLASS() class BOSSFIGHT_API ABFBaseCharacter : public ACharacter, public IAbilitySystemInterface { GENERATED_BODY() public: ABFBaseCharacter(); //~Begin IAbilitySystemInterface virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override; //~End IAbilitySystemInterface FORCEINLINE UBFAbilitySystemComponent* GetBFAbilitySystemComponent() const { return BFAbilitySystemComponent; } FORCEINLINE UBFAttributeSetBase* GetBFAttributeSet() const { return BFAttributeSet; } protected: //~ Begin APawn Interface. virtual void PossessedBy(AController* NewController) override; //~ End APawn Interface UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Ability System") UBFAbilitySystemComponent* BFAbilitySystemComponent; UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Ability System") UBFAttributeSetBase* BFAttributeSet; };🎮 ABFBaseCharacter.cpp
ABFBaseCharacter::ABFBaseCharacter() { PrimaryActorTick.bCanEverTick = false; PrimaryActorTick.bStartWithTickEnabled = false; GetMesh()->bReceivesDecals = false; BFAbilitySystemComponent = CreateDefaultSubobject<UBFAbilitySystemComponent>(TEXT("BFAbilitySystemComponent")); BFAttributeSet = CreateDefaultSubobject<UBFAttributeSetBase>(TEXT("BFAttributeSet")); } UAbilitySystemComponent* ABFBaseCharacter::GetAbilitySystemComponent() const { return GetBFAbilitySystemComponent(); } void ABFBaseCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); if (BFAbilitySystemComponent) { BFAbilitySystemComponent->InitAbilityActorInfo(this, this); } }위와 같이 코드를 작성하여, BaseCharacter를 상속 받은 캐릭터는 모드 AbilitySystemComponent를 소유 하고, Attribute값을 가질수 있게 되었다.

🎮 UBFGameplayAbilityBase.h
UENUM(BlueprintType) enum class EAbilityActivationPolicy : uint8 { OnTriggered, OnGiven }; class UBFAbilitySystemComponent; UCLASS() class BOSSFIGHT_API UBFGameplayAbility : public UGameplayAbility { GENERATED_BODY() protected: //~ Begin UGameplayAbility Interface virtual void OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) override; virtual void EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) override; //~ End UGameplayAbility Interface UPROPERTY(EditDefaultsOnly, Category = "Ability|AbilityActivationPolicy") EAbilityActivationPolicy AbilityActivationPolicy = EAbilityActivationPolicy::OnTriggered; UFUNCTION(BlueprintPure, Category = "BossFight|Ability") UBFAbilitySystemComponent* GetWrriorAbilitySystemComponentFromActorInfo() const; };🎮 UBFGameplayAbility.cpp
void UBFGameplayAbility::OnGiveAbility(const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilitySpec& Spec) { Super::OnGiveAbility(ActorInfo, Spec); if (AbilityActivationPolicy == EAbilityActivationPolicy::OnGiven) { if (ActorInfo && !Spec.IsActive()) { ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle, true); } } } void UBFGameplayAbility::EndAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, bool bReplicateEndAbility, bool bWasCancelled) { Super::EndAbility(Handle, ActorInfo, ActivationInfo, bReplicateEndAbility, bWasCancelled); if (AbilityActivationPolicy == EAbilityActivationPolicy::OnGiven) { if (ActorInfo && !bWasCancelled) { ActorInfo->AbilitySystemComponent->ClearAbility(Handle); } } } UBFAbilitySystemComponent* UBFGameplayAbilityBase::GetWrriorAbilitySystemComponentFromActorInfo() const { return GetAvatarActorFromActorInfo()->FindComponentByClass<UBFAbilitySystemComponent>(); }AbilityBase는 이후 사용할 모든 Ability의 Base 가 될 Class로 공통적인 기능을 구현 해주었다.
EAbilityActivationPolicy Enum을 통해 입력이나, GameplayEvent 등 특정 조건에 의하여 작동되는지, Ability가 부여 됨과 동시에 작동 하는지 구분하고 OnGiveAbility() 와 EndAbility()를 통해 제어 하도록 하였다.
또한 UBFAbilitySystemComponent를 반환 하는 기능을 구현하여, 필요할때 호출 할 수 있도록 하였다.
🎮 UBFPlayerAbility.h
class ABFPlayerCharacter; class ABFPlayerController; UCLASS() class BOSSFIGHT_API UBFPlayerAbility : public UBFGameplayAbilityBase { GENERATED_BODY() public: UFUNCTION(BlueprintPure, Category = "BossFight | Ability") ABFPlayerCharacter* GetPlayerFromActorInfo(); UFUNCTION(BlueprintPure, Category = "BossFight | Ability") ABFPlayerController* GetPlayerControllerFromActorInfo(); private: TWeakObjectPtr<ABFPlayerCharacter> CachedPlayer; TWeakObjectPtr<ABFPlayerController> CachedPlayerController; };🎮 UBFPlayerAbility.cpp
ABFPlayerCharacter* UBFPlayerAbility::GetPlayerFromActorInfo() { if (!CachedPlayer.IsValid()) { CachedPlayer = Cast<ABFPlayerCharacter>(CurrentActorInfo->AvatarActor); } return CachedPlayer.IsValid() ? CachedPlayer.Get() : nullptr; } ABFPlayerController* UBFPlayerAbility::GetPlayerControllerFromActorInfo() { if (!CachedPlayerController.IsValid()) { CachedPlayerController = Cast<ABFPlayerController>(CurrentActorInfo->PlayerController); } return CachedPlayerController.IsValid() ? CachedPlayerController.Get() : nullptr; }UBFPlayerAbility는 플레이어가 사용할 Ability로 PlayerCharacter나 Controller를 반환 하는 기능을 구현 하였다.
참고로 현재 작성한 BFPlayerController는 플레이어컨트롤러를 상속받기만 한 Class로 후에 TeamID를 통해 적대 여부를 구분하는 기능을 추가 할 예정이다.