Widget Attribute Tags

Mapping Tags to Attributes
직접 작성하는 방법
void UAttributeMenuWidgetController::BroadcastInitialValues()
{
UAuraAttributeSet* AS = CastChecked<UAuraAttributeSet>(AttributeSet);
check(AttributeInfo);
// 아래 구문을 모든 Attribute마다 작성해주어야 함.
FAuraAttributeInfo Info = AttributeInfo->FindAttributeInfoForTag(FAuraGameplayTag::Attribute_Primary_Strength);\
Info.AttributeValue = AS->GetStrength();
FAttributeInfoSignature.Broadcast(Info);
}Delegate 및 BindStatic()을 활용하는 방법
DECLARE_DELEGATE_RetVal(’반환형’, ‘델리게이트명’) 의 형태로 작성하면 됨.// AuraAttributeSet.h
DECLARE_DELEGATE_RetVal(FGameplayAttribute, FAttributeSignature);
TMap<FGameplayTag, FAttributeSignature> TagsToAttributes;
// AuraAttributeSet.cpp
UAuraAttributeSet::UAuraAttributeSet()
{
const FAuraGameplayTags& GameplayTags = FAuraGameplayTags::Get();
FAttributeSignature StrengthDelegate;
StrengthDelegate.BindStatic(GetStrengthAttribute);
TagsToAttributes.Add(GameplayTags.Attributes_Primary_Strength, StrengthDelegate);
}
void UAttributeMenuWidgetController::BroadcastInitialValues()
{
UAuraAttributeSet* AS = CastChecked<UAuraAttributeSet>(AttributeSet);
check(AttributeInfo);
for (auto& Pair : AS->TagsToAttributes)
{
FAuraAttributeInfo Info = AttributeInfo->FindAttributeInfoForTag(Pair.Key);
Info.AttributeValue = Pair.Value.Execute().GetNumericValue(AS);
AttributeInfoDelegate.Broadcast(Info);
}
}
TBaseStaticDelegateInstance를 활용하는 방법
TBaseStaticDelegateInstance를 직접 활용해 볼 수 있다.
BindStatic()의 매개변수와 함수 구현을 직접 살펴보면 아래와 같은 형태로 되어 있다.
```cpp
// 매개변수
BindStatic(typename TBaseStaticDelegateInstance<FuncType, UserPolicy, std::decay_t<VarTypes>...>::FFuncPtr InFunc, VarTypes&&... Vars)
/**
* Binds a raw C++ pointer global function delegate
*/
template <typename... VarTypes>
inline void BindStatic(typename TBaseStaticDelegateInstance<FuncType, UserPolicy, std::decay_t<VarTypes>...>::FFuncPtr InFunc, VarTypes&&... Vars)
{
new (*this) TBaseStaticDelegateInstance<FuncType, UserPolicy, std::decay_t<VarTypes>...>(InFunc, Forward<VarTypes>(Vars)...);
}
```
이 중에서 TBaseStaticDelegateInstance가 바로 함수를 묶는 함수 포인터의 역할을 한다.
또한 어차피 TBaseStaticDelegateInstance를 new로 생성하는 역할만 하고 있는 것을 볼 수 있다.
해당 클래스의 일부를 살펴보면 아래와 같다.
```cpp
/**
* Implements a delegate binding for regular C++ functions.
*/
template <typename FuncType, typename UserPolicy, typename... VarTypes>
class TBaseStaticDelegateInstance;
template <typename RetValType, typename... ParamTypes, typename UserPolicy, typename... VarTypes>
class TBaseStaticDelegateInstance<RetValType(ParamTypes...), UserPolicy, VarTypes...> : public TCommonDelegateInstanceState<RetValType(ParamTypes...), UserPolicy, VarTypes...>
{
private:
using Super = TCommonDelegateInstanceState<RetValType(ParamTypes...), UserPolicy, VarTypes...>;
using DelegateBaseType = typename UserPolicy::FDelegateExtras;
public:
using FFuncPtr = RetValType(*)(ParamTypes..., VarTypes...);
template <typename... InVarTypes>
explicit TBaseStaticDelegateInstance(FFuncPtr InStaticFuncPtr, InVarTypes&&... Vars)
: Super (Forward<InVarTypes>(Vars)...)
, StaticFuncPtr(InStaticFuncPtr)
{
check(StaticFuncPtr != nullptr);
}
// IDelegateInstance interface
// .....
private:
// C++ function pointer.
FFuncPtr StaticFuncPtr;
};
```
주석을 보면, 일반적인 C++ 함수에 대한 델리게이트 바인딩을 시행한다고 되어 있다.
즉, BindStatic()은 편리하게 사용할 수 있는 방법을 제공할 뿐이고, TBaseStaticDelegateInstance가 실질적인 델리게이트 바인딩을 시행한다고 보면 될 듯 하다.
그렇다면 굳이 델리게이트를 선언해서 BindStatic()으로 Attribute Getter 함수들을 묶기보다는, TBaseStaticDelegateInstance를 직접 사용한다면 훨씬 깔끔하게 처리가 가능할 것이다.
이 중에서도 FFuncPtr가 변수 이름 그대로 함수 포인터 역할을 한다.
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "AuraAttributeSet.generated.h"
#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)
USTRUCT()
struct FEffectProperties
{
// ...
};
UCLASS()
class AURA_API UAuraAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
UAuraAttributeSet();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) override;
TMap<FGameplayTag, TBaseStaticDelegateInstance<FGameplayAttribute(), FDefaultDelegateUserPolicy>::FFuncPtr> TagsToAttributes;
// ...
private:
void SetEffectProperties(const FGameplayEffectModCallbackData& Data, FEffectProperties& OutEffectProperties) const;
};
C++의 원시적인 함수 포인터
[반환형](*함수명)(매개변수)의 형태로 이루어짐.
따라서 FGameplayAttribute(*)()는 매개변수가 없고,
FGameplayAttribute를 반환하는 함수를 지칭하는 것.typedef, template/using/typename
typedef TBaseStaticDelegateInstance<FGameplayAttribute(), FDefaultDelegateUserPolicy>::FFuncPtr FAttributeFuncPtr; 와 같은 방식으로 선언하면, FAttributeFuncPtr가 TBaseStaticDelegateInstance<FGameplayAttribute(), FDefaultDelegateUserPolicy>::FFuncPtr와 동일한 역할을 한다.
직접 typename 없이 빌드를 시도해봤더니, 약 30초 정도 빌드 대기를 하다가 “ 'TBaseStaticDelegateInstance<T,FDefaultDelegateUserPolicy,>::FFuncPtr': 종속 이름이 형식이 아닙니다.”라는 위와 같은 오류가 잔뜩 출력되고 빌드가 실패하는 모습을 볼 수 있었다.
Responding to Attribute Changes
void UAttributeMenuWidgetController::BindCallbacksToDependencies()
{
UAuraAttributeSet* AS = CastChecked<UAuraAttributeSet>(AttributeSet);
for (auto& Pair : AS->TagsToAttributes)
{
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Pair.Value())
.AddLambda([this, Pair, AS](const FOnAttributeChangeData& Data)
{
FAuraAttributeInfo Info = AttributeInfo->FindAttributeInfoForTag(Pair.Key);
Info.AttributeValue = Pair.Value().GetNumericValue(AS);
AttributeInfoDelegate.Broadcast(Info);
}
);
}
}