这一篇文章是个人的实战经验记录,如果对基础性的内容不了解的,可以看我前面一篇文章对基础的概念以及内容的讲解。

设置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++里面去定义。

UE 5 GAS 在项目中处理AttributeSet相关_网络


上面是我在AS里面定义的两个生命相关的值,以及覆盖的两个函数回调。

UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_02


PreAttributeChange 主要是为了触发一个值的最大值被修改后,它的值也会按比例进行修改

UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_03


AdjustAttributeForMaxChange 就是为了实现这个效果的函数,这个也是从ActionRPG的项目里面抄过来的。

UE 5 GAS 在项目中处理AttributeSet相关_网络_04


PostGameplayEffectExecute 就是Base Value改变后触发的,我们可以在这个函数里面比如限制血量不会超过最大血量的值,也不会小于0的情况。

初始化Attribute

创建好了AS,接着要在角色身上使用并且初始化AS里面一些值,这里有三种方法:

  1. 第一种方法也不算方法,其实就是在AS类里面给它设置默认值
  2. UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_05

  3. 这是设置以后也算是初始化了相关属性的一些值。
  4. UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_06

  5. 我们还需要在OwnerActor里面进行初始化,需要在实例化ASC后面创建即可。这样我们就可以在ASC中去获取AS的值了。
  6. 第二种方法当然就是官方推荐的使用GE去初始化
  7. UE 5 GAS 在项目中处理AttributeSet相关_网络_07

  8. 创建一个GE,然后Duration Policy设置为Instant(直接修改Base Value),然后设置好需要修改的Attribute,Modifier Op设置为Override(覆盖),然后在下面的设置固定数值,就可以实现重新设置,只需要设置每个就可以实现覆盖。
  9. UE 5 GAS 在项目中处理AttributeSet相关_笔记_08

  10. 这里有个小细节,就是初始化时,推荐先修改最大值,然后再修改默认值,因为在C++里面,我们有个按比例进行修改默认值的,如果你先修改Health然后再修改MaxHealth,它会按比例放大的,然后再直接获取值会出现问题,所以我先设置的最大值,然后再设置就没这样的问题了。
  11. UE 5 GAS 在项目中处理AttributeSet相关_网络_09

  12. 设置完成以后,使用ApplyGameplayEffectToSelf节点,去设置即可。
  13. 使用DataTable设置默认值
  14. UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_10

  15. 这个需要开启在蓝图内读写ASC
  16. UE 5 GAS 在项目中处理AttributeSet相关_游戏_11

  17. 然后ASC细节里面会有一项AttributeTest,里面可以创建多个数组,代表可以初始化多个AS。Attributes选择AS的类,DefaultStartingTable则是选择创建的DataTable。
  18. UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_12

  19. 创建DataTable的时候,需要选择AttributeMetaData
  20. UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_13

  21. 行命名,要和在GE里面设置的命名保持一致,设置值时,就BaseValue有用,其它值估计是为了以后版本更新时使用,现在还没啥用。

获取AttributeSet的数据

UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_14


可以从ASC去获取属性的Base Value

UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_15


可以从ASC中获取Current Value我们还可以通过扩展c++接口来实现获取

UE 5 GAS 在项目中处理AttributeSet相关_笔记_16


比如在它的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);
}

类实现了以后,我们可以使用里面的两个方法:

UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_17


Listen for Attribute Change 可以监听一个属性的变动,返回了当前监听实例,以及变动后的回调,还有之前的值,变化后的值,如果值发生改变,就会触发OnAttributeChanged执行,只需要后面执行,每次数值改变,都会触发执行。

UE 5 GAS 在项目中处理AttributeSet相关_网络_18


Listen for Attribute Change可以传入一个数组,获取多个属性的变动然后可以通过Attribute返回值判断是哪个属性

UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_19


取消监听,那么记得调用End Task,一般在UI里面使用,可以在创建UI的时候绑定,然后把绑定的Task设置为变量,销毁的时候End Task即可。

设置AttributeSet的值

在GAS系统中,修改AttributeSet的值,需要使用GE去实现

UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_20


前面在初始化的时候,也讲了相应的配置。如果上面的值是Instant,那么这个GE被调用的时候,血量的值会瞬间掉1点。

接下来,介绍几个会在项目中使用到的场景。

持续掉血,每秒掉多少血量

UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_21


使用Has Duration,下面设置持续时间,在Period设置每过多少秒触发一次,Execute Periodic Effect on Application 意思为添加时是否触发一次。

UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_22


最简单的就是调用一下就行了,一般这个需要在GA里面去触发的,这一篇不包括GA相关。

根据等级设置血量加成

先创建一个CSV,可以先创建Excel文件,转csv

UE 5 GAS 在项目中处理AttributeSet相关_笔记_23


设置好文件以后

UE 5 GAS 在项目中处理AttributeSet相关_unreal engine 5_24


创建一个曲线表格

UE 5 GAS 在项目中处理AttributeSet相关_笔记_25


选择上面导入键,可以将csv文件导入,Row是作为值的名称,使用时需要选择哪个Row,而数字则代表当前的技能等级。

UE 5 GAS 在项目中处理AttributeSet相关_笔记_26


这里因为是加成,所以,运算修改为相乘,而ScalableFloatMagnitude的数值是从曲线表格里面获取到的值和此值相乘为最终的值。这个值再与现在的MaxHealth相乘得到了变化后的值。

后面设置了曲线表格,然后选择Row,然后就可以预览相应的值,这个预览的值是在将GE附加到ASC上面的时候设置。

UE 5 GAS 在项目中处理AttributeSet相关_游戏引擎_27


在附加的时候,我们可以设置Level等级,就是曲线表格里面的那个数值。