这一篇文章是个人的实战经验记录,如果对基础性的内容不了解的,可以看我前面一篇文章对基础的概念以及内容的讲解。
设置AttributeSet
使用GAS之前,首先需要设置参数集AS,这个是用于同步的一些参数,至于如何设置GAS,我之前的文章里面有讲,这里不赘述。
每个Attribute其实包含两个值 Base Value和Current Value。Base Value是在GE里面Instant设置属性时被修改的值,而Current Value是在GE的Infinite(永久修改)和Has Duration(具有时效性修改)时修改的,目的就是为了时效失效以后,能够将值的结果修改回去。当然获取值时都是获取的Current Value,如果没有时效性的影响,Base Value和Current Value值也是相等的。
AS里面主要还是为了创建一些值,并且由ASC调用GE去修改里面的值,但是这个类只能在C++里面去定义。
上面是我在AS里面定义的两个生命相关的值,以及覆盖的两个函数回调。
PreAttributeChange 主要是为了触发一个值的最大值被修改后,它的值也会按比例进行修改
AdjustAttributeForMaxChange 就是为了实现这个效果的函数,这个也是从ActionRPG的项目里面抄过来的。
PostGameplayEffectExecute 就是Base Value改变后触发的,我们可以在这个函数里面比如限制血量不会超过最大血量的值,也不会小于0的情况。
初始化Attribute
创建好了AS,接着要在角色身上使用并且初始化AS里面一些值,这里有三种方法:
- 第一种方法也不算方法,其实就是在AS类里面给它设置默认值
- 这是设置以后也算是初始化了相关属性的一些值。
- 我们还需要在OwnerActor里面进行初始化,需要在实例化ASC后面创建即可。这样我们就可以在ASC中去获取AS的值了。
- 第二种方法当然就是官方推荐的使用GE去初始化
- 创建一个GE,然后Duration Policy设置为Instant(直接修改Base Value),然后设置好需要修改的Attribute,Modifier Op设置为Override(覆盖),然后在下面的设置固定数值,就可以实现重新设置,只需要设置每个就可以实现覆盖。
- 这里有个小细节,就是初始化时,推荐先修改最大值,然后再修改默认值,因为在C++里面,我们有个按比例进行修改默认值的,如果你先修改Health然后再修改MaxHealth,它会按比例放大的,然后再直接获取值会出现问题,所以我先设置的最大值,然后再设置就没这样的问题了。
- 设置完成以后,使用ApplyGameplayEffectToSelf节点,去设置即可。
- 使用DataTable设置默认值
- 这个需要开启在蓝图内读写ASC
- 然后ASC细节里面会有一项AttributeTest,里面可以创建多个数组,代表可以初始化多个AS。Attributes选择AS的类,DefaultStartingTable则是选择创建的DataTable。
- 创建DataTable的时候,需要选择AttributeMetaData
- 行命名,要和在GE里面设置的命名保持一致,设置值时,就BaseValue有用,其它值估计是为了以后版本更新时使用,现在还没啥用。
获取AttributeSet的数据
可以从ASC去获取属性的Base Value
可以从ASC中获取Current Value我们还可以通过扩展c++接口来实现获取
比如在它的OwnerActor类里面增加获取属性的方法,其实就是返回AttributeSet的相关属性
还有一种就是使用AbilityTask实现监听接口,需要创建C++类来实现,这里我也是直接从官方文档案例里面扒的,把代码列出来。
AT_AttributeChanged.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintAsyncActionBase.h"
#include "AbilitySystemComponent.h"
#include "AT_AttributeChanged.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnAttributeChanged, FGameplayAttribute, Attribute, float, NewValue, float, OldValue);
/**
* Blueprint node to automatically register a listener for all attribute changes in an AbilitySystemComponent.
* Useful to use in UI.
*/
UCLASS(BlueprintType, meta = (ExposedAsyncProxy = AsyncTask))
class SURVIVALALONE_API UAT_AttributeChanged : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintAssignable)
FOnAttributeChanged OnAttributeChanged;
// Listens for an attribute changing.
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UAT_AttributeChanged* ListenForAttributeChange(UAbilitySystemComponent* AbilitySystemComponent, FGameplayAttribute Attribute);
// Listens for an attribute changing.
// Version that takes in an array of Attributes. Check the Attribute output for which Attribute changed.
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true"))
static UAT_AttributeChanged* ListenForAttributesChange(UAbilitySystemComponent* AbilitySystemComponent, TArray<FGameplayAttribute> Attributes);
// You must call this function manually when you want the AsyncTask to end.
// For UMG Widgets, you would call it in the Widget's Destruct event.
UFUNCTION(BlueprintCallable)
void EndTask();
protected:
UPROPERTY()
UAbilitySystemComponent* ASC;
FGameplayAttribute AttributeToListenFor;
TArray<FGameplayAttribute> AttributesToListenFor;
void AttributeChanged(const FOnAttributeChangeData& Data);
};
AT_AttributeChanged.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "AT_AttributeChanged.h"
UAT_AttributeChanged *UAT_AttributeChanged::ListenForAttributeChange(UAbilitySystemComponent *AbilitySystemComponent, FGameplayAttribute Attribute)
{
UAT_AttributeChanged *WaitForAttributeChangedTask = NewObject<UAT_AttributeChanged>();
WaitForAttributeChangedTask->ASC = AbilitySystemComponent;
WaitForAttributeChangedTask->AttributeToListenFor = Attribute;
if (!IsValid(AbilitySystemComponent) || !Attribute.IsValid())
{
WaitForAttributeChangedTask->RemoveFromRoot();
return nullptr;
}
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).AddUObject(WaitForAttributeChangedTask, &UAT_AttributeChanged::AttributeChanged);
return WaitForAttributeChangedTask;
}
UAT_AttributeChanged *UAT_AttributeChanged::ListenForAttributesChange(UAbilitySystemComponent *AbilitySystemComponent, TArray<FGameplayAttribute> Attributes)
{
UAT_AttributeChanged *WaitForAttributeChangedTask = NewObject<UAT_AttributeChanged>();
WaitForAttributeChangedTask->ASC = AbilitySystemComponent;
WaitForAttributeChangedTask->AttributesToListenFor = Attributes;
if (!IsValid(AbilitySystemComponent) || Attributes.Num() < 1)
{
WaitForAttributeChangedTask->RemoveFromRoot();
return nullptr;
}
for (FGameplayAttribute Attribute : Attributes)
{
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute).AddUObject(WaitForAttributeChangedTask, &UAT_AttributeChanged::AttributeChanged);
}
return WaitForAttributeChangedTask;
}
void UAT_AttributeChanged::EndTask()
{
if (IsValid(ASC))
{
ASC->GetGameplayAttributeValueChangeDelegate(AttributeToListenFor).RemoveAll(this);
for (FGameplayAttribute Attribute : AttributesToListenFor)
{
ASC->GetGameplayAttributeValueChangeDelegate(Attribute).RemoveAll(this);
}
}
SetReadyToDestroy();
MarkAsGarbage();
}
void UAT_AttributeChanged::AttributeChanged(const FOnAttributeChangeData &Data)
{
OnAttributeChanged.Broadcast(Data.Attribute, Data.NewValue, Data.OldValue);
}
类实现了以后,我们可以使用里面的两个方法:
Listen for Attribute Change 可以监听一个属性的变动,返回了当前监听实例,以及变动后的回调,还有之前的值,变化后的值,如果值发生改变,就会触发OnAttributeChanged执行,只需要后面执行,每次数值改变,都会触发执行。
Listen for Attribute Change可以传入一个数组,获取多个属性的变动然后可以通过Attribute返回值判断是哪个属性
取消监听,那么记得调用End Task,一般在UI里面使用,可以在创建UI的时候绑定,然后把绑定的Task设置为变量,销毁的时候End Task即可。
设置AttributeSet的值
在GAS系统中,修改AttributeSet的值,需要使用GE去实现
前面在初始化的时候,也讲了相应的配置。如果上面的值是Instant,那么这个GE被调用的时候,血量的值会瞬间掉1点。
接下来,介绍几个会在项目中使用到的场景。
持续掉血,每秒掉多少血量
使用Has Duration,下面设置持续时间,在Period设置每过多少秒触发一次,Execute Periodic Effect on Application 意思为添加时是否触发一次。
最简单的就是调用一下就行了,一般这个需要在GA里面去触发的,这一篇不包括GA相关。
根据等级设置血量加成
先创建一个CSV,可以先创建Excel文件,转csv
设置好文件以后
创建一个曲线表格
选择上面导入键,可以将csv文件导入,Row是作为值的名称,使用时需要选择哪个Row,而数字则代表当前的技能等级。
这里因为是加成,所以,运算修改为相乘,而ScalableFloatMagnitude的数值是从曲线表格里面获取到的值和此值相乘为最终的值。这个值再与现在的MaxHealth相乘得到了变化后的值。
后面设置了曲线表格,然后选择Row,然后就可以预览相应的值,这个预览的值是在将GE附加到ASC上面的时候设置。
在附加的时候,我们可以设置Level等级,就是曲线表格里面的那个数值。