1. 属性的位置 —— 属性集
创建属性集类GASAttributeSet
,继承自GAS提供的AttributeSet
,并添加生命值相关的关键属性:
#pragma once
#include "CoreMinimal.h"
#include "AbilitySystemComponent.h"
#include "AttributeSet.h"
#include "GASAttributeSet.generated.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 INSIDEGAS_API UGASAttributeSet : public UAttributeSet
{
GENERATED_BODY()
public:
UGASAttributeSet();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
public:
UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_Health)
FGameplayAttributeData Health;
ATTRIBUTE_ACCESSORS(UGASAttributeSet, Health)
UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_MaxHealth)
FGameplayAttributeData MaxHealth;
ATTRIBUTE_ACCESSORS(UGASAttributeSet, MaxHealth)
UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_HealthRegenRate)
FGameplayAttributeData HealthRegenRate;
ATTRIBUTE_ACCESSORS(UGASAttributeSet, HealthRegenRate)
UFUNCTION()
virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
UFUNCTION()
virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
UFUNCTION()
virtual void OnRep_HealthRegenRate(const FGameplayAttributeData& OldHealthRegenRate);
};
#include "GASAttributeSet.h"
#include "GameplayEffectExtension.h"
#include "Net/UnrealNetwork.h"
UGASAttributeSet::UGASAttributeSet()
{
}
void UGASAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION_NOTIFY(UGASAttributeSet, Health, COND_None, REPNOTIFY_Always);
}
void UGASAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UGASAttributeSet, Health, OldHealth);
}
void UGASAttributeSet::OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UGASAttributeSet, MaxHealth, OldMaxHealth);
}
void UGASAttributeSet::OnRep_HealthRegenRate(const FGameplayAttributeData& OldHealthRegenRate)
{
GAMEPLAYATTRIBUTE_REPNOTIFY(UGASAttributeSet, HealthRegenRate, OldHealthRegenRate);
}
其中ATTRIBUTE_ACCESSORS
是利用AttributeSet
提供的一系列宏为属性自动生成一些便利的函数,如初始化器、访问器、设置器等等,其本质上就是免去了自己去手写这些函数的麻烦。
然后将属性集挂载到角色身上,即在角色类GASCharacterBase
类中添加代码:
头文件
void OnHealthChanged(const FOnAttributeChangeData& OnAttributeChangeData);
void OnMaxHealthChanged(const FOnAttributeChangeData& OnAttributeChangeData);
void OnHealthRegenRateChanged(const FOnAttributeChangeData& OnAttributeChangeData);
UPROPERTY()
UGASAttributeSet* AttributeSet;
UFUNCTION(BlueprintCallable, Category = "GAS|Attributes")
float GetHealth() const;
UFUNCTION(BlueprintCallable, Category = "GAS|Attributes")
float GetMaxHealth() const;
UFUNCTION(BlueprintCallable, Category = "GAS|Attributes")
float GetHealthRegenRate() const;
并在cpp中添加:
AGASCharacterBase::AGASCharacterBase(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
...
AttributeSet = CreateDefaultSubobject<UGASAttributeSet>(TEXT("AttributeSet"));
}
float AGASCharacterBase::GetHealth() const
{
return AttributeSet->GetHealth();
}
float AGASCharacterBase::GetMaxHealth() const
{
return AttributeSet->GetMaxHealth();
}
float AGASCharacterBase::GetHealthRegenRate() const
{
return AttributeSet->GetHealthRegenRate();
}
这样,就实现了角色挂载属性集,属性集上维护了一系列属性。此时编译并运行编辑器,打开GAS的调试信息,可以看到属性以及正确挂载到角色身上。
2. 属性的变化
属性得有数值其本身也具备意义,而赋值(如初始化)的实现方法有非常灵活,可以利用前面AttributeSet
提供的初始化方法:
AttributeSet->InitHealth(100.0f);
也可以直接利用GE来进行初始化(GE也是与属性部分关系最为密切的模块):
在游玩开始时进行应用即可:
此时角色身上的属性就具备了初始值:
3. 简单的DOT伤害示例
这里我们使用HealthRegenRate
属性来制作一个简单的掉血Buff(当然这不是这个属性设计的目的,这里更多是演示属性与GE的联合使用)。
创建生命值流失的GE:
其中Coefficent
值设置为-2,他会和HealthRegenRate
共同作用,以Period
为周期,持续为Health
属性做减法运算。同样,在将GE应用给角色的GAS组件:
并辅以UI控件(ProgressBar
):
这样,一个简易的血条及掉血演示就完成了
4. 完善
在完成上面的测试后,很容易发现,血量在减到0后,依然不会停止。因为这显然并不合理。当然,我们可以通过卸载GE来停止这个Buff,但GAS提供了更加方便的接口,令我们可以直接对属性的变化做出限制。
在属性集类GASAttributeSet
中重载PostGameplayEffectExecute
:
virtual void PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data) override;
void UGASAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
Super::PostGameplayEffectExecute(Data);
if (Data.EvaluatedData.Attribute == GetHealthAttribute())
{
SetHealth(FMath::Clamp(GetHealth(), 0.0f, GetMaxHealth()));
}
}
如此进行简单的限制,保证血量不会跌破下限,也不会因为各种增益buff而突破上限。