Attribute의 GameplayTag와 함께 Attribute의 이름, 부가 설명, 값을 담는 구조체 FAuraAttributeInfo를 배열로 갖는 DataAsset을 생성하여, 게임 내에서 Attribute 및 그와 관련된 정보에 쉽게 접근할 수 있게 하려고 함.
AttributeDataAsset은 AttributeMenuWidgetController가 소유하여, 플레이어가 Attribute Menu 위젯을 보려고 할 때 사용할 목적으로 만드는 것임.
(태그 자체는 AuraGameplayTags의 FAuraGameplayTags에 추가하지만, 해당 태그에 대한 정보는 에디터에서 AttributeInfo DataAsset를 생성하고 그 곳에 추가하여 접근하는 방식임)
#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "GameplayTagContainer.h"
#include "AttributeInfo.generated.h"
USTRUCT(BlueprintType)
struct FAuraAttributeInfo
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FGameplayTag AttributeTag = FGameplayTag();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FText AttributeName = FText();
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
FText AttributeDescription = FText();
// 실제 값은 DataAsset에서 설정하는 것이 아니라 AttributeSet에 존재할 것이므로
// BlueprintReadOnly로 설정하고,
UPROPERTY(BlueprintReadOnly)
float AttributeValue = 0.f;
};
UCLASS()
class AURA_API UAttributeInfo : public UDataAsset
{
GENERATED_BODY()
public:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
TArray<FAuraAttributeInfo> AttributeInformation;
FAuraAttributeInfo FindAttributeInfoForTag(const FGameplayTag& AttributeTag, bool bLogNotFound = false) const;
};
#include "AbilitySystem/Data/AttributeInfo.h"
FAuraAttributeInfo UAttributeInfo::FindAttributeInfoForTag(const FGameplayTag& AttributeTag, bool bLogNotFound) const
{
for (const FAuraAttributeInfo& Info : AttributeInformation)
{
if (Info.AttributeTag.MatchesTagExact(AttributeTag))
{
return Info;
}
}
if (bLogNotFound)
{
UE_LOG(LogTemp, Error, TEXT("Attribute %s not Found on AttributeInfo %s"), *AttributeTag.ToString(), *GetNameSafe(this));
}
return FAuraAttributeInfo();
}
FindAttributeInfoForTag()로 DataAsset의 AttributeInformation 배열에 존재하는 GameplayTag 중 일치하는 것을 가져올 수 있음.
#include "CoreMinimal.h"
#include "UI/WidgetController/AuraWidgetController.h"
#include "AttributeMenuWidgetController.generated.h"
struct FAuraAttributeInfo;
class UAttributeInfo;
// FAuraAttributeInfo를 전파할 델리게이트 FAttributeInfoSignature 선언
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAttributeInfoSignature, const FAuraAttributeInfo&, Info);
UCLASS(BlueprintType, Blueprintable)
class AURA_API UAttributeMenuWidgetController : public UAuraWidgetController
{
GENERATED_BODY()
public:
virtual void BroadcastInitialValues() override;
virtual void BindCallbacksToDependencies() override;
UPROPERTY(BlueprintAssignable, Category = "GAS|Attributes")
FAttributeInfoSignature AttributeInfoDelegate;
protected:
// UAttributeInfo를 선언하고, 이를 에디터의 블루프린트에서 설정함.
UPROPERTY(EditDefaultsOnly)
TObjectPtr<UAttributeInfo> AttributeInfo;
};UOverlayWidgetController와 마찬가지로, WBP_AttributeMenu와 실제 참조할 데이터들 사이를 중재할 UAttributeMenuWidgetController를 생성함.
AttributeInfo DataAsset을 지니며, 이 정보를 FAttributeInfoSignature 델리게이트를 통해 전파함.
#include "UI/WidgetController/AttributeMenuWidgetController.h"
#include "AbilitySystem/AuraAttributeSet.h"
#include "AbilitySystem/Data/AttributeInfo.h"
#include "AuraGameplayTags.h"
void UAttributeMenuWidgetController::BroadcastInitialValues()
{
UAuraAttributeSet* AS = CastChecked<UAuraAttributeSet>(AttributeSet);
check(AttributeInfo);
FAuraAttributeInfo Info = AttributeInfo->FindAttributeInfoForTag(FAuraGameplayTags::Get().Attributes_Primary_Strength, true);
Info.AttributeValue = AS->GetStrength();
AttributeInfoDelegate.Broadcast(Info);
}
void UAttributeMenuWidgetController::BindCallbacksToDependencies()
{
}
AttributeInfo DataAsset에서 Attribute에 해당하는 실제 값을 AttributeSet에서 가져와서 설정해주어야 하므로, UAuraAttributeSet에 접근하여 FAuraGameplayTag의 GameplayTag 변수를 이용하여 AttributeInfo의 레퍼런스를 얻고, 거기에 AttributeValue만 따로 설정함.
(이미 AttributeName, AttributeTag, AttributeDescription은 에디터 상에서 다 설정했으므로)
AttributeValue까지 설정한 FAuraAttributeInfo를 델리게이트로 발송함.
AuraWidgetController의 변경 사항
WBP_Overlay, 즉 HUD용 위젯 같은 경우, 게임 중 계속해서 화면에 존재하는 위젯이므로, 플레이어 캐릭터인 Aura(AuraPlayerChraracter)가 플레이어 컨트롤러에 의해 Possessed 되거나 PlayerState가 Replicate 될 때 호출되는 InitAbilityActorInfo()에서 AuraHUD의 InitOverlay()를 호출하면서 OverlayWidgetController를 지정해주는 과정을 거침.
하지만, AttributeMenu의 경우 항상 게임 내에 존재하지도 않고, 게임이 시작되자마자 초기화 되는 것이 아닌, 플레이어가 버튼을 클릭하여 WBP_AttributeMenu가 화면에 출력될 때 AttributeMenuWidgetController를 지정하도록 하는 것이 효율적임.
따라서 WBP_AttributeMenu에서 직접 AttributeMenuWidgetController를 지정할 수 있도록 WidgetController의 레퍼런스를 얻어올 수 있는 블루프린트 호출 가능 함수를 만들되, 이를 AttributeMenu에서 직접 정의하지는 않도록 하는 것이 목적임.
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "UI/WidgetController/OverlayWidgetController.h"
#include "AuraAbilitySystemLibrary.generated.h"
UCLASS()
class AURA_API UAuraAbilitySystemLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintPure, Category = "AuraAbilitySystemLibrary|WidgetController")
static UOverlayWidgetController* GetOverlayWidgetController(const UObject* WorldContextObject);
UFUNCTION(BlueprintPure, Category = "AuraAbilitySystemLibrary|WidgetController")
static UAttributeMenuWidgetController* GetAttributeMenuWidgetController(const UObject* WorldContextObject);
};
#include "AbilitySystem/AuraAbilitySystemLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "Player/AuraPlayerState.h"
#include "UI/HUD/AuraHUD.h"
#include "UI/WidgetController/AuraWidgetController.h"
UOverlayWidgetController* UAuraAbilitySystemLibrary::GetOverlayWidgetController(const UObject* WorldContextObject)
{
if (APlayerController* PC = UGameplayStatics::GetPlayerController(WorldContextObject, 0))
{
if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PC->GetHUD()))
{
AAuraPlayerState* PS = PC->GetPlayerState<AAuraPlayerState>();
UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
UAttributeSet* AS = PS->GetAttributeSet();
const FWidgetControllerParams WidgetControllerParams(PC, PS, ASC, AS);
return AuraHUD->GetOverlayWidgetController(WidgetControllerParams);
}
}
return nullptr;
}
UAttributeMenuWidgetController* UAuraAbilitySystemLibrary::GetAttributeMenuWidgetController(const UObject* WorldContextObject)
{
if (APlayerController* PC = UGameplayStatics::GetPlayerController(WorldContextObject, 0))
{
if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PC->GetHUD()))
{
AAuraPlayerState* PS = PC->GetPlayerState<AAuraPlayerState>();
UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
UAttributeSet* AS = PS->GetAttributeSet();
const FWidgetControllerParams WidgetControllerParams(PC, PS, ASC, AS);
return AuraHUD->GetAttributeMenuWidgetController(WidgetControllerParams);
}
}
return nullptr;
}