GameplayAbilitySystem入门与实战(二):属性
前言
前一篇已经完成了初始化,角色可以在服务端和客户端正确触发技能,那么这一篇我们开始研究如何声明我们GAS
框架内的属性
暗黑3属性图镇楼
属性类
GAS
常用的属性类是FGameplayAttribute
和FGameplayAttributeData
FGameplayAttributeData
此结构体内有两个float
变量, 表示Base
和Current
两个值, 一般情况下是一致的. 多数游戏中的生命值,魔法值等多数属性都可以使用此属性
这里请看如下关键代码
1 | FAggregatorRef* RefPtr = AttributeAggregatorMap.Find(Attribute); |
上面的代码是在GameplayEffect.cpp
;中的SetAttributeBaseValue()
函数体中的, 本函数设置了Base
值,然后InternalUpdateNumericalAttribute()
设置了与Base
一样的Current
值, 从除非你自己创建了FAggregatorRef
类, 该类通过FActiveGameplayEffectsContainer::FindOrCreateAttributeAggregator()
创建,此方法是私有的, 实测通过FActiveGameplayEffectsContainer::CaptureAttributeForGameplayEffect()
可以在外部创建, 那么使用起来就有点略微麻烦了,而且如果使用默认的FAggregatorRef
类还是会饶了一圈通过OnAttributeAggregatorDirty()
再执行InternalUpdateNumericalAttribute()
把Current
值给设置为与Base
值一样的值.
当然你强行拿到FGameplayAttributeData
直接SetCurrentValue()
是可以改变的, 但是无法通过GE
来改到Current
值,而且没有属性的事件响应
对此处没有过多研究,如有错误请执教
FGameplayAttribute
相比而言只有一个浮点值, 对于一些辅助属性或者UI之类的属性可以用此属性
- 事件响应
1 | AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute()).AddUObject(this, &APlayerState::HealthChanged); |
1 | virtual void HealthChanged(const FOnAttributeChangeData& Data); |
用上述两个方法来绑定生命属性更改以后的回调,我们可以在绑定的事件处理比如UI之类的逻辑
后面会创建一个下图所示的异步节点来监听属性改变, 方便在各个地方得到属性数据
属性集/AttributeSet
顾名思义,属性集就是多个属性的容器,通常在拥有类的构造函数中创建,当然目前必须用C++
例如我们也可以在CharacterBase
的构造函数构造此类
1 | UPROPERTY() |
1 | AttributeSet = CreateDefaultSubobject<USRAttributeSetBase>(TEXT("AttributeSet")); |
声明
- 辅助宏
AttributeSet.h
中为我们提供了几个很有用的辅助宏, 用于方便我们在cpp
中get
,set
,init
等属性操作
1 |
我们再用ATTRIBUTE_ACCESSORS
宏包裹4个宏
然后用如下方法申明一个属性
1 | UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_Health) |
测试可以在cpp
中调用如下方法
1 | this->InitHealth(100); |
注意: 这个是cpp中的函数,在蓝图中无法使用
在蓝图中可以通过如下方式获取, 不能Set
同步
如果属性设置了同步(Rep)
那么可以在实现的时候用宏GAMEPLAYATTRIBUTE_REPNOTIFY
来处理属性的同步
1 | void UVGAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) |
因为
OnRep
函数是UFUNCTION()
,所以因为UE的限制, 没办法用宏来快速声明和定义, 所以属性很多的话只能手动一个个声明定义
最后,同步的属性需要在GetLifetimeReplicatedProps
函数内用宏DOREPLIFETIME_CONDITION_NOTIFY
处理同步,REPNOTIFY_Always
属性意味着服务器值过来以后一定会走Rep
函数, 默认情况如果值不变不用调用;
后面两个参数可以不填, 默认值是
COND_None,REPNOTIFY_OnChanged
如果是类似MetaAttribute
类的非同步属性, 那么此函数也会取消同步
1 | void UVGAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const |
动态添加/移除
属性集也可以动态的添加和移除
1 | AbilitySystemComponent->SpawnedAttributes.AddUnique(WeaponAttributeSetPointer); |
预处理/PreAttributeChange
属性在正在修改之前会到函数PreAttributeChange()
这里比较适合对NewValue
进行Clamp
处理,比如
1 | if (Attribute == GetMoveSpeedAttribute()) |
或者我们在这里对修改生命和魔法的最大值做一个处理, 请思考一般Moba或RPG等游戏中最大值修改以后, 当前的值也会按照百分比修改
1 | void USRAttributeSetBase::AdjustAttributeForMaxChange(FGameplayAttributeData& AffectedAttribute, const FGameplayAttributeData& MaxAttribute, float NewMaxValue, const FGameplayAttribute& AffectedAttributeProperty) |
然后在预处理中执行
1 | void USRAttributeSetBase::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) |
PostGameplayEffectExecute
PostGameplayEffectExecute()
函数会在瞬间执行的GameplayEffect
修改到属性的时候执行
这里可以做属性做一些额外的操作,比如对一些临时属性进行处理然后反馈到最终角色上, 我们后面会在这里做对临时的伤害值进行处理后, 修改角色的生命值等操作.
因为还涉及到很多后续的内容, 这里先不展开