• 13.3 GAS与属性


    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);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    #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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    其中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;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    并在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();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这样,就实现了角色挂载属性集,属性集上维护了一系列属性。此时编译并运行编辑器,打开GAS的调试信息,可以看到属性以及正确挂载到角色身上。

    在这里插入图片描述

    2. 属性的变化

    属性得有数值其本身也具备意义,而赋值(如初始化)的实现方法有非常灵活,可以利用前面AttributeSet提供的初始化方法:

    AttributeSet->InitHealth(100.0f);
    
    • 1

    也可以直接利用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()));
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如此进行简单的限制,保证血量不会跌破下限,也不会因为各种增益buff而突破上限。

    参考:

    属性 - Attributes

    属性集 - Attribute Set

    游戏效果 - Gameplay Effects

  • 相关阅读:
    乌云遮住了天空
    LeetCode第317场周赛打卡
    后端工程师——Java工程师面试小结
    用半天时间从零开始复习前端之html
    面试了20+前端大厂,整理出的面试题
    高并发系统设计架构方案
    查看哪个docker环境在占用gpu
    Linux常用命令
    springboot项目生成war包并部署到Tomcat服务器
    开启摄像头镜像无效-左右镜像相反
  • 原文地址:https://blog.csdn.net/xcinkey/article/details/127775215