Calmer的文章

  • 首页
  • 文章归档
  • 关于页面

  • 搜索
体验游戏 笔记 推荐 工具链 工具使用 小游戏 插件 UI 软件 教程

UE5中GAS GE属性计算(GE优化)

发表于 2023-06-05 | 分类于 游戏开发 | 0 | 阅读次数 2979

前言

使用GAS时一定会用到GE,那么就会面临

  • 巨量GE
  • 维护困难
  • 重复性配置工作
  • 无法通过简单配置多属性与临时值混合计算

一. GE实例与CDO Apply的区别

CDO直接Apply会比较直观和方便
GE实例Apply可以合并一些较小差异的GE,避免大量GE文件


二. GE属性值捕捉方案

因为GE的CDO上只能支持一些简单的属性计算配置,要想实现复杂一些的,就需要自己去实现GEEC或者MMC,而实现计算,首先就需要对属性值进行捕捉。以下是可实行的一些方案。

方案1(官方):直接在GEEC或MMC的h文件中定义结构体,静态确定需要捕获的属性值

  • 实现思路
    1. 定义结构体GDDamageStatics,声明需要捕捉的属性值
    2. 声明静态方法DamageStatics
    3. 在GEEC或MMC的构造函数中往关联属性数组中添加需要捕获的属性定义
  • 参考代码
// Declare the attributes to capture and define how we want to capture them from the Source and Target.
struct GDDamageStatics
{
	DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);
	DECLARE_ATTRIBUTE_CAPTUREDEF(PhysicsAttack)
	DECLARE_ATTRIBUTE_CAPTUREDEF(Damage);

	GDDamageStatics()
	{
		// Snapshot happens at time of GESpec creation

		// We're not capturing anything from the Source in this example, but there could be like AttackPower attributes that you might want.

		// Capture optional Damage set on the damage GE as a CalculationModifier under the ExecutionCalculation
		DEFINE_ATTRIBUTE_CAPTUREDEF(UGDAttributeSetBase, Damage, Source, true);
		DEFINE_ATTRIBUTE_CAPTUREDEF(UGDAttributeSetBase, PhysicsAttack, Source, false);
		// Capture the Target's Armor. Don't snapshot.
		DEFINE_ATTRIBUTE_CAPTUREDEF(UGDAttributeSetBase, Armor, Target, false);
	}
};

static const GDDamageStatics& DamageStatics()
{
	static GDDamageStatics DStatics;
	return DStatics;
}

UGDDamageExecCalculation::UGDDamageExecCalculation()
{
	RelevantAttributesToCapture.Add(DamageStatics().DamageDef);
	RelevantAttributesToCapture.Add(DamageStatics().ArmorDef);
	RelevantAttributesToCapture.Add(DamageStatics().PhysicsAttackDef);
}

方案2:通过Caller逐一传入

因为GE实例,可以通过Caller来进行float值的设置与获取
image.png

  • 实现思路
    1. 创建GESpecHandle
      image.png
    2. 封装一个需要传递的数据结构
    3. 封装通过Caller传入,传出解析数据结构的函数(进一步可以使用反射来实现)
  • 参考代码
USTRUCT()
struct FCustomAttributeStruct
{
	GENERATED_USTRUCT_BODY()
public:
	float HP;
	float MP;
	float PhyAttack;
};


void UCalmerBlueprintLibrary::SetCustomAttribute(const FGameplayEffectSpecHandle& Handle,
	const FCustomAttributeStruct& AttributeStruct)
{
	Handle.Data->SetSetByCallerMagnitude(TEXT("HP"), AttributeStruct.HP);
	Handle.Data->SetSetByCallerMagnitude(TEXT("MP"), AttributeStruct.MP);
	Handle.Data->SetSetByCallerMagnitude(TEXT("PhyAttack"), AttributeStruct.PhyAttack);
}

void UCalmerBlueprintLibrary::GetCustomAttribute(const FGameplayEffectSpecHandle& Handle,
	FCustomAttributeStruct& AttributeStruct)
{
	AttributeStruct.HP = Handle.Data->GetSetByCallerMagnitude(TEXT("HP"));
	AttributeStruct.MP = Handle.Data->GetSetByCallerMagnitude(TEXT("MP"));
	AttributeStruct.PhyAttack = Handle.Data->GetSetByCallerMagnitude(TEXT("PhyAttack"));
}


方案3:通过EffectContext传递(推荐)

坑点:赋值时序可能导致SourceObject值未被设置,可能是蓝图蜜糖陷阱导致

1.通过SourceObject(简单)

  • 自定义UObject
UCLASS(BlueprintType)
class GASDOCUMENTATION_API UEffectSourceDataObject : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY(BlueprintReadWrite)
	float Damage;
	UPROPERTY(BlueprintReadWrite)
	float PhyAttack;
	UPROPERTY(BlueprintReadWrite)
	float DamageMagnification;
};
  • 封装蓝图方法
void UGDBlueprintLibrary::SetEffectContextSourceObject(FGameplayEffectContextHandle GameplayEffectContextHandle,
	UObject* Object)
{
	GameplayEffectContextHandle.Get()->AddSourceObject(Object);
}

2. 自定义GameplayEffectContext, 继承GameplayEffectContext, 并重写Globals中的分配函数

  • SubClass GameplayEffectContext
    FCaGamePlayEffectContext.h
USTRUCT()
struct FCaGamePlayEffectContext : public FGameplayEffectContext
{
	GENERATED_USTRUCT_BODY()
public:
	virtual FGameplayAbilityTargetDataHandle GetTargetData()
	{
		return TargetData;
	}

	virtual void AddTargetData(const FGameplayAbilityTargetDataHandle& TargetDataHandle)
	{
		TargetData.Append(TargetDataHandle);
	}

	virtual FCommonTargetData* GetCommonData()
	{
		return CommonTargetData.Get();
	}

	virtual const FCommonTargetData* GetCommonData() const
	{
		return const_cast<FCaGamePlayEffectContext*>(this)->GetCommonData();
	}

	virtual void AddCommonTargetData(const FCommonTargetData& InCommonTargetData)
	{
		if (CommonTargetData.IsValid())
		{
			CommonTargetData.Reset();
		}

		check(!CommonTargetData.IsValid());
		CommonTargetData = TSharedPtr<FCommonTargetData>(new FCommonTargetData(InCommonTargetData));
	}
	
	virtual UScriptStruct* GetScriptStruct() const override
	{
		return FCaGamePlayEffectContext::StaticStruct();
	}

	virtual FCaGamePlayEffectContext* Duplicate() const override
	{
		FCaGamePlayEffectContext* NewContext = new FCaGamePlayEffectContext();
		*NewContext = *this;
		NewContext->AddActors(Actors);
		if (GetHitResult())
		{
			// Does a deep copy of the hit result
			NewContext->AddHitResult(*GetHitResult(), true);
		}
		//参考HitResult
		if(GetCommonData())
		{
			NewContext->AddCommonTargetData(*GetCommonData());
		}
		// Shallow copy of TargetData, is this okay?
		NewContext->TargetData.Append(TargetData);
		return NewContext;
	}

	virtual bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess) override;

protected:
	FGameplayAbilityTargetDataHandle TargetData;
	TSharedPtr<FCommonTargetData> CommonTargetData;
};


template<>
struct TStructOpsTypeTraits< FCaGamePlayEffectContext > : public TStructOpsTypeTraitsBase2< FCaGamePlayEffectContext >
{
	enum
	{
		WithNetSerializer = true,
		WithCopy = true		// Necessary so that TSharedPtr<FHitResult> Data is copied around
	};
};

注意: FCommonTargetData为自定义数据结构体, FGameplayAbilityTargetDataHandle为官方提供结构体

.cpp

bool FCaGamePlayEffectContext::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess)
{
	return Super::NetSerialize(Ar, Map, bOutSuccess) && TargetData.NetSerialize(Ar, Map, bOutSuccess);
}
  • 继承UAbilitySystemGlobals,修改配置
    UCaAbilitySystemGlobals.h
#pragma once

#include "CoreMinimal.h"
#include "AbilitySystemGlobals.h"
#include "CaAbilitySystemGlobals.generated.h"

UCLASS()
class GASDOCUMENTATION_API UCaAbilitySystemGlobals : public UAbilitySystemGlobals
{
	GENERATED_BODY()
public:
	UCaAbilitySystemGlobals();

	static UCaAbilitySystemGlobals& GSGet()
	{
		return dynamic_cast<UCaAbilitySystemGlobals&>(Get());
	}

	virtual FGameplayEffectContext* AllocGameplayEffectContext() const override;
};

.cpp

#include "GAS/CaAbilitySystemGlobals.h"
#include "Data/EffectSourceDataObject.h"

UCaAbilitySystemGlobals::UCaAbilitySystemGlobals()
{
}

FGameplayEffectContext* UCaAbilitySystemGlobals::AllocGameplayEffectContext() const
{
	return new FCaGamePlayEffectContext();
}

配置修改

[/Script/GameplayAbilities.AbilitySystemGlobals]
GameplayCueNotifyPaths="/Game/GASDocumentation/Characters"
AbilitySystemGlobalsClassName="/Script/GASDocumentation.CaAbilitySystemGlobals"
  • 自定义数据结构,参考HitResult
USTRUCT(BlueprintType)
struct FCommonTargetData:public FGameplayAbilityTargetData
{
	GENERATED_USTRUCT_BODY()

public:
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Targeting)
	float Damage;
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Targeting)
	float PhyAttack;
	UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Targeting)
	float DamageMagnification;
	
	// FCommonTargetData& operator=(FCommonTargetData&& Other) { Damage = Other.Damage; PhyAttack= Other.PhyAttack; DamageMagnification = Other.DamageMagnification; return *this; }
	// FCommonTargetData& operator=(const FCommonTargetData& Other) { Damage = Other.Damage; PhyAttack= Other.PhyAttack; DamageMagnification = Other.DamageMagnification; return *this; }
	
	virtual UScriptStruct* GetScriptStruct() const override
	{
		return FCommonTargetData::StaticStruct();
	}

	virtual FString ToString() const override
	{
		return TEXT("FCommonTargetData");
	}

	bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};

template<>
struct TStructOpsTypeTraits<FCommonTargetData> : public TStructOpsTypeTraitsBase2<FCommonTargetData>
{
	enum
	{
		WithNetSerializer = true	// For now this is REQUIRED for FGameplayAbilityTargetDataHandle net serialization to work
	};
};
  • 添加蓝图静态方法,以添加自定义数据
    .h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "GameplayEffect.h"
#include "Data/EffectSourceDataObject.h"
#include "GDBlueprintLibrary.generated.h"

/**
 * 
 */
UCLASS()
class GASDOCUMENTATION_API UGDBlueprintLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

public:
	//方案1:通过SourceObject在context中去传递内容
	UFUNCTION(BlueprintCallable)
	static void SetEffectContextSourceObject(FGameplayEffectContextHandle GameplayEffectContextHandle, UObject* Object);

	//通过自定义Context,自定义字段传递
	UFUNCTION(BlueprintCallable)
	static void SetEffectContextCommonTargetData(FGameplayEffectContextHandle GameplayEffectContextHandle,
	                                const FCommonTargetData& CommonTargetData);

	UFUNCTION(BlueprintCallable)
	static void AddEffectContextTargetData(FGameplayEffectContextHandle GameplayEffectContextHandle,
	                                       const FGameplayAbilityTargetDataHandle& GameplayAbilityTargetDataHandle);

	UFUNCTION(BlueprintCallable)
	static FGameplayAbilityTargetDataHandle GetEffectContextTargetData(
		FGameplayEffectContextHandle GameplayEffectContextHandle);
};

.cpp

#include "GDBlueprintLibrary.h"

void UGDBlueprintLibrary::SetEffectContextSourceObject(FGameplayEffectContextHandle GameplayEffectContextHandle,
	UObject* Object)
{
	GameplayEffectContextHandle.Get()->AddSourceObject(Object);
}

void UGDBlueprintLibrary::SetEffectContextCommonTargetData(FGameplayEffectContextHandle GameplayEffectContextHandle,
                                              const FCommonTargetData& CommonTargetData)
{
	FCaGamePlayEffectContext* CaGamePlayEffectContext = static_cast<FCaGamePlayEffectContext*>(GameplayEffectContextHandle.Get());
	if(CaGamePlayEffectContext)
	{
		CaGamePlayEffectContext->AddCommonTargetData(CommonTargetData);
	}
}

void UGDBlueprintLibrary::AddEffectContextTargetData(FGameplayEffectContextHandle GameplayEffectContextHandle,
                                                     const FGameplayAbilityTargetDataHandle& GameplayAbilityTargetDataHandle)
{
	FCaGamePlayEffectContext* CaGamePlayEffectContext = static_cast<FCaGamePlayEffectContext*>(GameplayEffectContextHandle.Get());
	if (CaGamePlayEffectContext)
	{
		CaGamePlayEffectContext->AddTargetData(GameplayAbilityTargetDataHandle);
	}
}

FGameplayAbilityTargetDataHandle UGDBlueprintLibrary::GetEffectContextTargetData(
	FGameplayEffectContextHandle GameplayEffectContextHandle)
{
	FCaGamePlayEffectContext* CaGamePlayEffectContext = static_cast<FCaGamePlayEffectContext*>(GameplayEffectContextHandle.Get());
	if (CaGamePlayEffectContext)
	{
		return CaGamePlayEffectContext->GetTargetData();
	}
	return FGameplayAbilityTargetDataHandle();
}


三. GE MMC与GEEC的封装

1. MMC的封装(单属性修改规则)

.h文件

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "WMModMagnitudeCalculationBase.generated.h"

/**
 * 
 */
UCLASS()
class WATCHMAN_API UWMModMagnitudeCalculationBase : public UGameplayModMagnitudeCalculation
{
	GENERATED_BODY()
public:

	FGameplayEffectSpec* GameplayEffectSpec;
	
	virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;	

	UFUNCTION(BlueprintCallable, BlueprintPure, Category="UWMModMagnitudeCalculationBase", meta=(HideSelfPin="true", AdvancedDisplay="bSnapshot"))
	bool GetCapturedAttr(FGameplayAttribute Attribute, EGameplayEffectAttributeCaptureSource CaptureSource, float& Value, bool bSnapshot=true);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category="UWMModMagnitudeCalculationBase", meta=(HideSelfPin="true"))
	void GetGESpec(FGameplayEffectSpec& Spec);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category="UWMModMagnitudeCalculationBase", meta=(HideSelfPin="true"))
	void GetCallerValueByTag(FGameplayTag GameplayTag, float& OutValue);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category="UWMModMagnitudeCalculationBase", meta=(HideSelfPin="true"))
	void GetCallerValueByName(FName GameName, float& OutValue);
	
	UFUNCTION(BlueprintImplementableEvent)
	float GetFinalCalculationValue();
};

.cpp文件

// Fill out your copyright notice in the Description page of Project Settings.


#include "WMModMagnitudeCalculationBase.h"

float UWMModMagnitudeCalculationBase::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
	UWMModMagnitudeCalculationBase* WMModMagnitudeCalculationBase = const_cast<UWMModMagnitudeCalculationBase*>(this);
	WMModMagnitudeCalculationBase->GameplayEffectSpec = const_cast<FGameplayEffectSpec*>(&Spec);
	return WMModMagnitudeCalculationBase->GetFinalCalculationValue();
}

bool UWMModMagnitudeCalculationBase::GetCapturedAttr(FGameplayAttribute Attribute,
                                                     EGameplayEffectAttributeCaptureSource CaptureSource, float& Value,
                                                     bool bSnapshot)
{
	const FGameplayTagContainer* SourceTags = GameplayEffectSpec->CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = GameplayEffectSpec->CapturedTargetTags.GetAggregatedTags();
	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;
	bool const IsGetSuccess = GetCapturedAttributeMagnitude(
		FGameplayEffectAttributeCaptureDefinition(Attribute, CaptureSource, bSnapshot), *GameplayEffectSpec,
		EvaluationParameters, Value);
	if (!IsGetSuccess)
	{
		UE_LOG(LogTemp, Error,
		       TEXT(
			       "UWMModMagnitudeCalculationBase::GetCapturedAttr Failed, AttrName: %s, CaptureSource:%d, bSnapShot:%d"
		       ), *Attribute.AttributeName, CaptureSource, bSnapshot)
	}
	return IsGetSuccess;
}

void UWMModMagnitudeCalculationBase::GetCallerValueByTag(FGameplayTag GameplayTag, float& OutValue)
{
	OutValue = GameplayEffectSpec->GetSetByCallerMagnitude(GameplayTag);
}

void UWMModMagnitudeCalculationBase::GetCallerValueByName(FName GameName, float& OutValue)
{
	OutValue = GameplayEffectSpec->GetSetByCallerMagnitude(GameName);
}

void UWMModMagnitudeCalculationBase::GetGESpec(FGameplayEffectSpec& Spec)
{
	Spec = *GameplayEffectSpec;
}

2. GEEC的封装(多属性修改规则)

.h文件

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameplayEffectExecutionCalculation.h"
#include "WMGEExecutionCalculationBase.generated.h"

/**
 * 
 */
UCLASS()
class WATCHMAN_API UWMGEExecutionCalculationBase : public UGameplayEffectExecutionCalculation
{
	GENERATED_BODY()
public:

	UWMGEExecutionCalculationBase();
	
	FGameplayEffectCustomExecutionParameters* ExecutionParameters;

	FGameplayEffectCustomExecutionOutput* CurrentOutExecutionOutput;
	
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="WMGEExecutionCalculation", meta=(HideSelfPin="true"))
	void GetGESpec(FGameplayEffectSpec& GameplayEffectSpec);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category="WMGEExecutionCalculation", meta=(HideSelfPin="true"))
	void GetCallerValueByTag(FGameplayTag GameplayTag, float& OutValue);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category="WMGEExecutionCalculation", meta=(HideSelfPin="true"))
	void GetCallerValueByName(FName GameName, float& OutValue);
	
	UFUNCTION(BlueprintCallable, BlueprintPure, Category="WMGEExecutionCalculation", meta=(HideSelfPin="true", AdvancedDisplay="bSnapshot"))
	bool GetCapturedAttr(FGameplayAttribute Attribute, EGameplayEffectAttributeCaptureSource CaptureSource, float& Value, bool bSnapshot=true);

	UFUNCTION(BlueprintCallable, Category="WMGEExecutionCalculation", meta=(HideSelfPin="true", AdvancedDisplay="ModOp"))
	void AddExecutionOutput(FGameplayAttribute GameplayAttribute, float Magnitude, TEnumAsByte<EGameplayModOp::Type> ModOp = EGameplayModOp::Additive);
	
	virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override;

	UFUNCTION(BlueprintImplementableEvent)
	void ExecuteInBlueprint();
};

.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "WMGEExecutionCalculationBase.h"

UWMGEExecutionCalculationBase::UWMGEExecutionCalculationBase()
{
#if WITH_EDITOR
	UEnum* EnumPtr = FindObject<UEnum>(ANY_PACKAGE, TEXT("EGameplayEffectAttributeCaptureSource"), true);
	if(EnumPtr)
	{
		EnumPtr->SetMetaData(TEXT("DisplayName"), TEXT("攻击者"), 0);
		EnumPtr->SetMetaData(TEXT("DisplayName"), TEXT("受击者"), 1);
	}
#endif
}

void UWMGEExecutionCalculationBase::GetGESpec(FGameplayEffectSpec& GameplayEffectSpec)
{
	GameplayEffectSpec = ExecutionParameters->GetOwningSpec();
}

void UWMGEExecutionCalculationBase::GetCallerValueByTag(FGameplayTag GameplayTag, float& OutValue)
{
	const FGameplayEffectSpec& Spec = ExecutionParameters->GetOwningSpec();
	OutValue = Spec.GetSetByCallerMagnitude(GameplayTag);
}

void UWMGEExecutionCalculationBase::GetCallerValueByName(FName GameName, float& OutValue)
{
	const FGameplayEffectSpec& Spec = ExecutionParameters->GetOwningSpec();
	OutValue = Spec.GetSetByCallerMagnitude(GameName);
}

bool UWMGEExecutionCalculationBase::GetCapturedAttr(FGameplayAttribute Attribute,
                                                    EGameplayEffectAttributeCaptureSource CaptureSource,
                                                    float& Value, bool bSnapshot)
{
	Value = 0.0f;
	const FGameplayEffectSpec& Spec = ExecutionParameters->GetOwningSpec();
	const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;
	bool const IsGetSuccess = ExecutionParameters->AttemptCalculateCapturedAttributeMagnitude(
		FGameplayEffectAttributeCaptureDefinition(Attribute, CaptureSource, bSnapshot), EvaluationParameters, Value);
	if(!IsGetSuccess)
	{
		UE_LOG(LogTemp, Error, TEXT("UWMGEExecutionCalculationBase::GetCapturedAttr Failed, AttrName: %s, CaptureSource:%d, bSnapShot:%d"), *Attribute.AttributeName, CaptureSource, bSnapshot)
	}
	return IsGetSuccess;
}

void UWMGEExecutionCalculationBase::AddExecutionOutput(
	FGameplayAttribute GameplayAttribute, float Magnitude, TEnumAsByte<EGameplayModOp::Type> ModOp)
{
	CurrentOutExecutionOutput->AddOutputModifier(FGameplayModifierEvaluatedData(GameplayAttribute, ModOp, Magnitude));
}

void UWMGEExecutionCalculationBase::Execute_Implementation(
	const FGameplayEffectCustomExecutionParameters& ExecutionParams,
	FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
	// Super::Execute_Implementation(ExecutionParams, OutExecutionOutput);
	UWMGEExecutionCalculationBase* WMExecutionCalculationBase = const_cast<UWMGEExecutionCalculationBase*>(this);
	WMExecutionCalculationBase->ExecutionParameters =const_cast<FGameplayEffectCustomExecutionParameters*>(&ExecutionParams);
	WMExecutionCalculationBase->CurrentOutExecutionOutput = &OutExecutionOutput;
	WMExecutionCalculationBase->ExecuteInBlueprint();
}

3. 扩展:全属性捕捉封装(不推荐)

为了省去设置需要捕捉的值的步骤,需要在继承后,构造函数中去反射遍历Attribute类所有属性,然后添加到捕捉列表

  • 核心代码,构造函数中
UCaGEECAllAttrBase::UCaGEECAllAttrBase()
{
	for (TFieldIterator<FProperty> It(UCaAttributeSetBase::StaticClass()); It; ++It)
	{
		FGameplayAttribute GameplayAttribute = FGameplayAttribute(*It);
		if(GameplayAttribute.IsValid())
		{
			RelevantAttributesToCapture.Add(FGameplayEffectAttributeCaptureDefinition(GameplayAttribute,EGameplayEffectAttributeCaptureSource::Source, true));
			RelevantAttributesToCapture.Add(FGameplayEffectAttributeCaptureDefinition(GameplayAttribute,EGameplayEffectAttributeCaptureSource::Target, true));
		}
		UE_LOG(LogTemp,Log,TEXT("AttrbuteName:%s"), *It->GetName());
	}
}

4. 简单使用方法

  1. 在蓝图中创建并继承上述基类(GEEC或MMC)
  2. 在ClassDefault配置需要捕捉的值
  3. 蓝图Graph中Get需要的值,可以是属性值,可以是Caller值,也可以通过EffectContext传递过来的值
  4. 书写计算公式
  5. AddExecutionOutPut,输出到对应属性上(MMC在GE外部就设置好了,只能对单个属性做修改,GEEC可以对多个属性做修改)

5. GEEC和MMC的区别

MMC做了预测而GEEC没有
MMC针对单一属性,GEEC对多属性

6. 蓝图库方法(操作GEEC与MMC)

略


四. GE的合并(优化)

  • 结合传值和自定义MMC,GEEC,可以合并大量GE,减少GE数量
  • GE也可以通过工具去生成,修改CDO

封装常见通用GE

  • 赋予Tag的GE
  • 冷却时间的GE
  • 消耗的GE

五. 参考文档

https://github.com/tranek/GASDocumentation#concepts-ge-tags
《Lyra》
《ActionRPG》
《GASShotter》
《GASDocumentation》

  • 本文作者: Calmer
  • 本文链接: https://mytechplayer.com/archives/ue5中gasge属性计算
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
# 笔记
UE5开发问题记录(二)2023.3.15_2023.6.6
UE5中GAS接入增强输入(EnhancedInput)
  • 文章目录
  • 站点概览
Calmer

Calmer

88 日志
7 分类
10 标签
RSS
Creative Commons
0%
© 2020 — 2025 Calmer
由 Halo 强力驱动
蜀ICP备20010026号-1川公网安备51019002006543
Copyright © 2020-2025 Calmer的文章