AbilitySystemComponent
/**
* UAbilitySystemComponent
*
* A component to easily interface with the 3 aspects of the AbilitySystem:
*
* GameplayAbilities:
* -Provides a way to give/assign abilities that can be used (by a player or AI for example)
* -Provides management of instanced abilities (something must hold onto them)
* -Provides replication functionality
* -Ability state must always be replicated on the UGameplayAbility itself, but UAbilitySystemComponent provides RPC replication
* for the actual activation of abilities
*
* GameplayEffects:
* -Provides an FActiveGameplayEffectsContainer for holding active GameplayEffects
* -Provides methods for applying GameplayEffects to a target or to self
* -Provides wrappers for querying information in FActiveGameplayEffectsContainers (duration, magnitude, etc)
* -Provides methods for clearing/remove GameplayEffects
*
* GameplayAttributes
* -Provides methods for allocating and initializing attribute sets
* -Provides methods for getting AttributeSets
*
*/AbilitySystemComponent 헤더 초반에 쓰여진 코멘트.
UAbilitySystemComponent의 주 목적은 AbilitySystem의 3요소인 GameplayAbilities, GameplayEffects, GameplayAttributes와의 간편한 상호작용을 위한 인터페이스로서의 역할이라고 쓰여져 있다.
GameplayAbilities, GameplayEffect, AttributeSet에서 발생하는 특정 상황의 이벤트에 대한 델리게이트도 제공한다.
/** Called when a targeting actor rejects target confirmation */
DECLARE_MULTICAST_DELEGATE_OneParam(FTargetingRejectedConfirmation, int32);
// 타겟 액터가 타겟 확인을 거부했을 시
/** Called when ability fails to activate, passes along the failed ability and a tag explaining why */
DECLARE_MULTICAST_DELEGATE_TwoParams(FAbilityFailedDelegate, const UGameplayAbility*, const FGameplayTagContainer&);
// 어빌리티 작동이 실패했을 때, 해당 어빌리티와 실패 이유에 대한 정보를 FGameplayTagContainer로 반환
/** Called when ability ends */
DECLARE_MULTICAST_DELEGATE_OneParam(FAbilityEnded, UGameplayAbility*);
// 어빌리티 종료 시
/** Notify interested parties that ability spec has been modified */
DECLARE_MULTICAST_DELEGATE_OneParam(FAbilitySpecDirtied, const FGameplayAbilitySpec&);
// 어빌리티 스펙에 변경점이 생겼을 시
/** Notifies when GameplayEffectSpec is blocked by an ActiveGameplayEffect due to immunity */
DECLARE_MULTICAST_DELEGATE_TwoParams(FImmunityBlockGE, const FGameplayEffectSpec& /*BlockedSpec*/, const FActiveGameplayEffect* /*ImmunityGameplayEffect*/);
// 이펙트가 현재 활성화 된 이펙트의 면역 효과로 인해 블록됐을 시
// UAbilitySystemComponent 클래스 내부
/** Used to register callbacks to ability-key input */
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAbilityAbilityKey, /*UGameplayAbility*, Ability, */int32, InputID);
/** Used to register callbacks to confirm/cancel input */
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAbilityConfirmOrCancel);
/** Delegate for when an effect is applied */
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);
// 이펙트가 적용됐을 시
델리게이트들은 GameplayEffectTypes.h, GameplayAbilityTypes.h 등에 선언된 것들도 있다.
(FOnActiveGameplayEffectStackChange, FGameplayAbilityDelegate 등)
```cpp
FORCEINLINE bool HasMatchingGameplayTag(FGameplayTag TagToCheck) const override
{
return GameplayTagCountContainer.HasMatchingGameplayTag(TagToCheck);
}
FORCEINLINE bool HasAllMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override
{
return GameplayTagCountContainer.HasAllMatchingGameplayTags(TagContainer);
}
FORCEINLINE bool HasAnyMatchingGameplayTags(const FGameplayTagContainer& TagContainer) const override
{
return GameplayTagCountContainer.HasAnyMatchingGameplayTags(TagContainer);
}
FORCEINLINE void GetOwnedGameplayTags(FGameplayTagContainer& TagContainer) const override
{
TagContainer.Reset();
TagContainer.AppendTags(GameplayTagCountContainer.GetExplicitGameplayTags());
}
```GameplayEffectDelegates
// AbilitySystemComponent.h
/** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
/** Called on server whenever a GE is applied to someone else. This includes instant and duration based GEs. */
FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget;
/** Called on both client and server whenever a duraton based GE is added (E.g., instant GEs do not trigger this). */
FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf;
/** Called on server whenever a periodic GE executes on self */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf;
/** Called on server whenever a periodic GE executes on target */
FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget;
// AuraAbilitySystemComponent.cpp
void UAuraAbilitySystemComponent::AbilityActorInfoSet()
{
OnGameplayEffectAppliedDelegateToSelf.AddUObject(this, &UAuraAbilitySystemComponent::EffectApplied);
}
void UAuraAbilitySystemComponent::EffectApplied(UAbilitySystemComponent* AbilitySystemComponent, const FGameplayEffectSpec& EffectSpec, FActiveGameplayEffectHandle ActiveEffectHandle)
{
// Broadcast Informations for Widgets
FGameplayTagContainer TagContainer;
EffectSpec.GetAllAssetTags(TagContainer);
EffectAssetTags.Broadcast(TagContainer);
}
```cpp
void AAuraPlayerCharacter::InitAbilityActorInfo()
{
AAuraPlayerState* AuraPlayerState = GetPlayerState<AAuraPlayerState>();
check(AuraPlayerState);
AuraPlayerState->GetAbilitySystemComponent()->InitAbilityActorInfo(AuraPlayerState, this);
Cast<UAuraAbilitySystemComponent>(AuraPlayerState->GetAbilitySystemComponent())->AbilityActorInfoSet();
AbilitySystemComponent = AuraPlayerState->GetAbilitySystemComponent();
AttributeSet = AuraPlayerState->GetAttributeSet();
if (AAuraPlayerController* AuraPlayerController = Cast<AAuraPlayerController>(GetController()))
{
if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(AuraPlayerController->GetHUD()))
{
AuraHUD->InitOverlay(AuraPlayerController, AuraPlayerState, AbilitySystemComponent, AttributeSet);
}
}
}
void AAuraEnemyCharacter::InitAbilityActorInfo()
{
AbilitySystemComponent->InitAbilityActorInfo(this, this);
Cast<UAuraAbilitySystemComponent>(AbilitySystemComponent)->AbilityActorInfoSet();
}
```람다식 작성 방법 [Capture](Parameters){ ... function ... }
Cast<UAuraAbilitySystemComponent>(AbilitySystemComponent)
->EffectAssetTags.AddLambda(
[this](const FGameplayTagContainer& AssetTags)
{
for (const FGameplayTag& Tag : AssetTags)
{
// Tag에 따른 처리...
}
});
언리얼 델리게이트에서는 람다 함수를 바인딩 하는 방법으로 AddLambda()를 제공한다.
람다는 익명 함수이므로, 해당 함수는 ‘프로젝트 내’에 존재한다는 것은 알지만, 설령 해당 함수가 OverlayWidgetController.cpp에 작성되었다 한들 특정 클래스 내부에 존재한다는 사실은 모르는 상태이다.
UI Widget Data Table
GameplayTag 정보를 이용한 메세지 출력을 위한 기반 작업
OverlayWidgetController.h에 FUIWidget 구조체를 선언한다.
FUIWidget 구조체는 각종 위젯들이 OverlayWidgetController로부터 정보를 받아오기 위한 델리게이트 FMessageWidgetRowSignature로 반환할 정보들을 묶어두었다.
USTRUCT(BlueprintType)
struct FUIWidgetRow : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FGameplayTag MessageTag = FGameplayTag();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
FText Message = FText::GetEmpty();
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TSubclassOf<UAuraUserWidget> MessageWidget;
UPROPERTY(EditAnywhere, BlueprintReadOnly)
UTexture2D* Image = nullptr;
};
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMessageWidgetRowSignature, FUIWidgetRow, Row);
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Widget Data")
TObjectPtr<UDataTable> MessageWidgetDataTable;
또한, DataTable 변수인 MessageWidgetDataTable을 선언하고, 이를 에디터의 BP_OverlayWidgetController에서 할당해준다.
FUIWidget 구조체를 통해 생성한 데이터 테이블이 필요하므로 이를 선언하고 세팅해준다.
각 메세지 위젯이 노출시킬 메세지, 이미지 등을 세팅하며, Row의 이름은 태그의 이름과 동일하게 세팅해야 한다.

- 이렇게 세팅한 데이터 테이블은 FMessageWidgetRowSignature 바인드 함수에서 사용한다.
```cpp
Cast<UAuraAbilitySystemComponent>(AbilitySystemComponent)->EffectAssetTags.AddLambda(
[this](const FGameplayTagContainer& AssetTags)
{
for (const FGameplayTag& Tag : AssetTags)
{
FGameplayTag MessageTag = FGameplayTag::RequestGameplayTag(FName("Message"));
if (Tag.MatchesTag(MessageTag))
{
// Find Row From MessageWidgetDataTable
const FUIWidgetRow* Row = GetDataTableRowByTag<FUIWidgetRow>(MessageWidgetDataTable, Tag);
MessageWidgetRowDelegate.Broadcast(*Row);
}
}
});
```
델리게이트는 블루프린트 WBP_Overlay에서 바인딩한다.
