断几个地方
ULyraDamageExecution::Execute_Implementation
ULyraHealthSet::PreGameplayEffectExecute
ULyraHealthSet::PostGameplayEffectExecute
ULyraHealthSet::PreAttributeBaseChange
ULyraHealthSet::PreAttributeChange
ULyraHealthSet::PostAttributeChange
Execute_Implementation
首先激活的是 Execute_Implementation
FActiveGameplayEffectHandle UAbilitySystemComponent::ApplyGameplayEffectSpecToTarget(const FGameplayEffectSpec &Spec, UAbilitySystemComponent *Target, FPredictionKey PredictionKey)
{
SCOPE_CYCLE_COUNTER(STAT_AbilitySystemComp_ApplyGameplayEffectSpecToTarget);
UAbilitySystemGlobals& AbilitySystemGlobals = UAbilitySystemGlobals::Get();
if (!AbilitySystemGlobals.ShouldPredictTargetGameplayEffects())
{
// If we don't want to predict target effects, clear prediction key
PredictionKey = FPredictionKey();
}
FActiveGameplayEffectHandle ReturnHandle;
if (Target)
{
ReturnHandle = Target->ApplyGameplayEffectSpecToSelf(Spec, PredictionKey);
}
return ReturnHandle;
}
ApplyGameplayEffectSpecToTarget 最后都会转发到某个ASC的 ApplyGameplayEffectSpecToSelf
参数的GE的 Spec 和 预测key
FActiveGameplayEffectHandle UAbilitySystemComponent::ApplyGameplayEffectSpecToSelf(const FGameplayEffectSpec &Spec, FPredictionKey PredictionKey)
{
// 这里是一些各种能否apply的检查
else if (Spec.Def->DurationPolicy == EGameplayEffectDurationType::Instant)
{
// This is a non-predicted instant effect (it never gets added to ActiveGameplayEffects)
ExecuteGameplayEffect(*OurCopyOfSpec, PredictionKey);
}
void UAbilitySystemComponent::ExecuteGameplayEffect(FGameplayEffectSpec &Spec, FPredictionKey PredictionKey)
{
// ...
ActiveGameplayEffects.ExecuteActiveEffectsFrom(Spec, PredictionKey);
}
这里我们注意到了 这个对象
UPROPERTY(Replicated)
FActiveGameplayEffectsContainer ActiveGameplayEffects;
这个 FActiveGameplayEffectsContainer 是个 FFastArraySerializer
那么我们找 NetDeltaSerialize 里面的 Tarray来找到 Item
UPROPERTY()
TArray<FActiveGameplayEffect> GameplayEffects_Internal;
好了继续看
void FActiveGameplayEffectsContainer::ExecuteActiveEffectsFrom(FGameplayEffectSpec &Spec, FPredictionKey PredictionKey)
{
// ...
// 第一段代码就是执行所有的 modifier
// InternalExecuteMod 可以先不急着看,因为马上就还有
for (int32 ModIdx = 0; ModIdx < SpecToUse.Modifiers.Num(); ++ModIdx)
{
const FGameplayModifierInfo& ModDef = SpecToUse.Def->Modifiers[ModIdx];
// ... check
FGameplayModifierEvaluatedData EvalData(ModDef.Attribute, ModDef.ModifierOp, SpecToUse.GetModifierMagnitude(ModIdx));
ModifierSuccessfullyExecuted |= InternalExecuteMod(SpecToUse, EvalData);
}
// 遍历所有 Executions 去执行
for (const FGameplayEffectExecutionDefinition& CurExecDef : SpecToUse.Def->Executions)
{
if (CurExecDef.CalculationClass)
{
// 执行 execution
ExecCDO->Execute(ExecutionParams, ExecutionOutput); // ULyraDamageExecution::Execute_Implementation 跑到了
// ULyraDamageExecution::Execute_Implementation 往 ExecutionOutput里面加了
// FGameplayModifierEvaluatedData(ULyraHealthSet::GetDamageAttribute(), EGameplayModOp::Additive, DamageDone)
// 这个 modifier
// 对着这个modifier去改属性
for (FGameplayModifierEvaluatedData& CurExecMod : OutModifiers)
{
// ...
// modifier 的逻辑 InternalExecuteMod
ModifierSuccessfullyExecuted |= InternalExecuteMod(SpecToUse, CurExecMod);
}
// ...
}
if (bRunConditionalEffects)
{
// ...
}
}
}
在 ULyraDamageExecution::Execute_Implementation 的最后,我们修改了 OutExecutionOutput.AddOutputModifier
if (DamageDone > 0.0f)
{
// Apply a damage modifier, this gets turned into - health on the target
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(ULyraHealthSet::GetDamageAttribute(), EGameplayModOp::Additive, DamageDone));
}
交给了 InternalExecuteMod
bool FActiveGameplayEffectsContainer::InternalExecuteMod(FGameplayEffectSpec& Spec, FGameplayModifierEvaluatedData& ModEvalData)
{
// 所以这里Spec 是近战的 GE, ModEvalData 是 ULyraHealthSet
bool bExecuted = false;
// ... 拿到 AttributeSet
if (AttributeSet)
{
FGameplayEffectModCallbackData ExecuteData(Spec, ModEvalData, *Owner);
/**
* This should apply 'gamewide' rules. Such as clamping Health to MaxHealth or granting +3 health for every point of strength, etc
* PreAttributeModify 可以返回false来阻止本次修改
*/
// !!! pre
if (AttributeSet->PreGameplayEffectExecute(ExecuteData))
{
// 执行修改属性
ApplyModToAttribute(ModEvalData.Attribute, ModEvalData.ModifierOp, ModEvalData.Magnitude, &ExecuteData);
FGameplayEffectModifiedAttribute* ModifiedAttribute = Spec.GetModifiedAttribute(ModEvalData.Attribute);
if (!ModifiedAttribute)
{
// If we haven't already created a modified attribute holder, create it
ModifiedAttribute = Spec.AddModifiedAttribute(ModEvalData.Attribute);
}
ModifiedAttribute->TotalMagnitude += ModEvalData.Magnitude;
// !!!post
AttributeSet->PostGameplayEffectExecute(ExecuteData);
bExecuted = true;
}
}
return bExecuted;
}
下一个断点是属性变化前后就在上面 ApplyModToAttribute 进去
void FActiveGameplayEffectsContainer::ApplyModToAttribute(const FGameplayAttribute &Attribute, TEnumAsByte<EGameplayModOp::Type> ModifierOp, float ModifierMagnitude, const FGameplayEffectModCallbackData* ModData)
{
float CurrentBase = GetAttributeBaseValue(Attribute);
float NewBase = FAggregator::StaticExecModOnBaseValue(CurrentBase, ModifierOp, ModifierMagnitude);
SetAttributeBaseValue(Attribute, NewBase);
}
void FActiveGameplayEffectsContainer::SetAttributeBaseValue(FGameplayAttribute Attribute, float NewBaseValue)
{
const UAttributeSet* Set = Owner->GetAttributeSubobject(Attribute.GetAttributeSetClass());
float OldBaseValue = 0.0f;
Set->PreAttributeBaseChange(Attribute, NewBaseValue); // <==== pre change
// 如果我们使用新的属性,就应该始终更新它们的基准值
bool bIsGameplayAttributeDataProperty = FGameplayAttribute::IsGameplayAttributeDataProperty(Attribute.GetUProperty());
if (bIsGameplayAttributeDataProperty)
{
const FStructProperty* StructProperty = CastField<FStructProperty>(Attribute.GetUProperty());
FGameplayAttributeData* DataPtr = StructProperty->ContainerPtrToValuePtr<FGameplayAttributeData>(const_cast<UAttributeSet*>(Set));
OldBaseValue = DataPtr->GetBaseValue();
DataPtr->SetBaseValue(NewBaseValue); // <==== 修改值
}
else
{
// 如果这是一个简单的浮点值,那么基准值等于当前值(除非存在下面提到的聚合器)
OldBaseValue = Owner->GetNumericAttribute(Attribute);
}
FAggregatorRef* RefPtr = AttributeAggregatorMap.Find(Attribute);
if (RefPtr)
{
FAggregator* Aggregator = RefPtr->Get();
check(Aggregator);
// There is an aggregator for this attribute, so set the base value. The dirty callback chain
// will update the actual AttributeSet property value for us.
OldBaseValue = Aggregator->GetBaseValue();
Aggregator->SetBaseValue(NewBaseValue);
}
// if there is no aggregator set the current value (base == current in this case)
else
{
InternalUpdateNumericalAttribute(Attribute, NewBaseValue, nullptr);
}
Set->PostAttributeBaseChange(Attribute, OldBaseValue, NewBaseValue); //《===
}
到此,我们整个流程清晰了,但是细节有不太理解我们需要看看一些类。
FActiveGameplayEffectsContainer
USTRUCT()
struct FActiveGameplayEffectsContainer : public FFastArraySerializer
{
GENERATED_USTRUCT_BODY();
// 拥有者和拥有者是否权威
UAbilitySystemComponent* Owner;
bool OwnerIsNetAuthority;
// 字面意思,GE被删除了事件,大概翻了下,从FastArray同步删除的时候,还有容器里面东西被删的时候触发
FOnGivenActiveGameplayEffectRemoved OnActiveGameplayEffectRemovedDelegate;
/** Our active list of Effects. Do not access this directly (Even from internal functions!) Use GetNumGameplayEffect() / GetGameplayEffect() ! */
// 这个就是FastArray的 Item,并且让你别直接用,因为有一些是 等待中的GE。例如在GE中添加GE,基于作用域锁,这些不会马上加进来,而是丢进一个Pending的链表。
// GetActiveGameplayEffect 接口封装了这种复杂性
UPROPERTY()
TArray<FActiveGameplayEffect> GameplayEffects_Internal;
// ApplyModToAttribute 开始的时候缓存一下,然后修改数值,然后清空
// 用于GE修改属性时吗,向下游如 Attrbute 的回调函数,传递上下文信息
const struct FGameplayEffectModCallbackData* CurrentModcallbackData;
// ...
}
struct FGameplayEffectModCallbackData
{
const struct FGameplayEffectSpec& EffectSpec;
struct FGameplayModifierEvaluatedData& EvaluatedData; // 包含了计算前后的数据,Attribute修改的属性,Op 修改类型(加/乘/覆盖) Magnitude最终数值
class UAbilitySystemComponent &Target; // 承受GE的目标 Target的ASC
};
USTRUCT()
struct FActiveGameplayEffectsContainer : public FFastArraySerializer
{
// ...
/*
Aggregator(聚合器), GAS 属性由 BaseValue 加上各种 modifier 决定,负责聚合数值和各种modifer的对象 FAggregator
FAggregatorRef 是对 FAggregator 的智能指针引用
存储了所有当前被修改过的属性对应的聚合器
*/
TMap<FGameplayAttribute, FAggregatorRef> AttributeAggregatorMap;
/*
属性变化监听事件
例如UI可以 AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(Attribute) 来新增对于HP属性变化的监听
*/
TMap<FGameplayAttribute, FOnGameplayAttributeValueChange> AttributeValueChangeDelegates;
/*
有些 GE 的数值不是固定的,而是通过 GameplayModMagnitudeCalculation (MMC) 类动态计算的(例如:伤害 = 力量 * 0.5 + 武器攻击力)
这就产生了一个依赖问题:如果“力量”变了,那么那个依赖于力量的“伤害 Buff”的数值也需要实时更新。
这个 Map 记录了哪些正在生效的 ActiveGE 依赖于哪些自定义计算类 (MMC)
当外部条件变化时(比如属性变动),系统可以通过查这个表,找到所有受影响的 GE,并通知它们重新计算数值(Recompute Magnitude)
*/
TMap<FObjectKey, FCustomModifierDependencyHandle> CustomMagnitudeClassDependencies;
/*
很多游戏中的 Buff 是可以堆叠的(比如“流血”效果叠 5 层)。
堆叠规则通常需要知道:这是谁释放的?
Key:UGameplayEffect 的指针(Def),即是哪种 GE。
Value:当前容器中所有该类型 GE 的 Handle 列表。
当一个新的 GE 试图应用时,系统需要快速知道“我之前有没有对这个目标施加过同类效果?”,
如果有,就要进行堆叠逻辑(加层数或刷新时间),而不是添加一个新的 GE 实例。这个 Map 加速了这个查找过程
*/
TMap<TWeakObjectPtr<UGameplayEffect>, TArray<FActiveGameplayEffectHandle> > SourceStackingMap;
// pending的链表
FActiveGameplayEffect* PendingGameplayEffectHead; // Head of pending GE linked list
FActiveGameplayEffect** PendingGameplayEffectNext; // Points to the where to store the next pending GE (starts pointing at head, as more are added, points further down the list).
USTRUCT(BlueprintType)
struct FActiveGameplayEffect : public FFastArraySerializerItem
{
/** 唯一ID,不同步 */
FActiveGameplayEffectHandle Handle;
// 运行时的GE
UPROPERTY()
FGameplayEffectSpec Spec;
如何理解
FGameplayEffectContextHandle ContextHandle = SourceASC->MakeEffectContext();
ContextHandle.AddHitResult(HitResult);
if (DamageEffectClass)
{
FGameplayEffectSpecHandle SpecHandle = SourceASC->MakeOutgoingSpec(DamageEffectClass, 1.0f, ContextHandle);
if (SpecHandle.IsValid())
{
SourceASC->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
}
}
例如这个 DamageEffectClass 是 UGameplayEffect,是一个静态模板,相当于数据资产。
运行时MakeOutgoingSpec 通过包装上下文创建了一个 FGameplayEffectSpec 并拿到他的 Handle (FGameplayEffectSpecHandle)
在 ApplyGameplayEffectSpecToTarget 之后,就变成 FActiveGameplayEffect开始生效了
回来
USTRUCT(BlueprintType)
struct FActiveGameplayEffect : public FFastArraySerializerItem
{
/** 唯一ID,不同步 */
FActiveGameplayEffectHandle Handle;
// 运行时的GE
UPROPERTY()
FGameplayEffectSpec Spec;
// 预测用的key
UPROPERTY()
FPredictionKey PredictionKey;
// 由于GE授予的GA的句柄
UPROPERTY()
TArray<FGameplayAbilitySpecHandle> GrantedAbilityHandles;
// ... 其他好像不是很特别
};
到这里我们apply GE To Self 的流程直接干到这里了
void FActiveGameplayEffectsContainer::ExecuteActiveEffectsFrom(FGameplayEffectSpec &Spec, FPredictionKey PredictionKey)
{
// ...
// 还有一些modifier
// 遍历所有 Executions 去执行
for (const FGameplayEffectExecutionDefinition& CurExecDef : SpecToUse.Def->Executions)
{
if (CurExecDef.CalculationClass)
{
// 执行 execution
ExecCDO->Execute(ExecutionParams, ExecutionOutput); // ULyraDamageExecution::Execute_Implementation 跑到了
// ULyraDamageExecution::Execute_Implementation 往 ExecutionOutput里面加了
// FGameplayModifierEvaluatedData(ULyraHealthSet::GetDamageAttribute(), EGameplayModOp::Additive, DamageDone)
// 这个 modifier
// 对着这个modifier去改属性
for (FGameplayModifierEvaluatedData& CurExecMod : OutModifiers)
{
// ...
// modifier 的逻辑 InternalExecuteMod
ModifierSuccessfullyExecuted |= InternalExecuteMod(SpecToUse, CurExecMod);
}
// ...
}
if (bRunConditionalEffects)
{
// ...
}
}
}
这里我想仔细看看 Execute 的前后
if (CurExecDef.CalculationClass)
{
const UGameplayEffectExecutionCalculation* ExecCDO = CurExecDef.CalculationClass->GetDefaultObject<UGameplayEffectExecutionCalculation>();
check(ExecCDO);
// Run the custom execution
FGameplayEffectCustomExecutionParameters ExecutionParams(SpecToUse, CurExecDef.CalculationModifiers, Owner, CurExecDef.PassedInTags, PredictionKey);
FGameplayEffectCustomExecutionOutput ExecutionOutput;
ExecCDO->Execute(ExecutionParams, ExecutionOutput);
bRunConditionalEffects = ExecutionOutput.ShouldTriggerConditionalGameplayEffects();
// Execute any mods the custom execution yielded
TArray<FGameplayModifierEvaluatedData>& OutModifiers = ExecutionOutput.GetOutputModifiersRef();
这里面ExecutionParams能拿到很多东西,GE的 Spec,Context
void UEqZeroDamageExecution::Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const
{
#if WITH_SERVER_CODE
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
FEqZeroGameplayEffectContext* TypedContext = FEqZeroGameplayEffectContext::ExtractEffectContext(Spec.GetContext());
check(TypedContext);
const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
FAggregatorEvaluateParameters EvaluateParameters;
EvaluateParameters.SourceTags = SourceTags;
EvaluateParameters.TargetTags = TargetTags;
float BaseDamage = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BaseDamageDef, EvaluateParameters, BaseDamage);
// ... 复杂的伤害计算
// 合并伤害值
const float DamageDone = FMath::Max(BaseDamage * DistanceAttenuation * PhysicalMaterialAttenuation * DamageInteractionAllowedMultiplier, 0.0f);
if (DamageDone > 0.0f)
{
// 作用到Damage上,后面HP就会响应的被修改
// 会作用到 Damage属性的变更流程
OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(UEqZeroHealthSet::GetDamageAttribute(), EGameplayModOp::Additive, DamageDone));
}
#endif // #if WITH_SERVER_CODE
}
这个读伤害的过程我们仔细看看
FAggregatorEvaluateParameters EvaluateParameters;
EvaluateParameters.SourceTags = SourceTags;
EvaluateParameters.TargetTags = TargetTags;
float BaseDamage = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BaseDamageDef, EvaluateParameters, BaseDamage);
DamageStatics().BaseDamageDef 是啥
struct FDamageStatics
{
FGameplayEffectAttributeCaptureDefinition BaseDamageDef;
FDamageStatics()
{
BaseDamageDef = FGameplayEffectAttributeCaptureDefinition(UEqZeroCombatSet::GetBaseDamageAttribute(), EGameplayEffectAttributeCaptureSource::Source, true);
}
};
例如
ATTRIBUTE_ACCESSORS(UEqZeroCombatSet, BaseDamage); 展开后
public:
// 1. 获取属性元数据(最重要!)
// 用于由 AttributeName 获取真正的 UProperty,或者在 Execution 中注册属性捕获
static FGameplayAttribute GetBaseDamageAttribute()
{
return FGameplayAttribute(UEqZeroCombatSet::StaticClass()->FindPropertyByName(FName("BaseDamage")));
}
// 2. 获取当前数值(Getter)
// 给你一个简单的 float,而不是复杂的 FGameplayAttributeData 结构体
float GetBaseDamage() const
{
return BaseDamage.GetCurrentValue();
}
// 3. 设置数值(Setter)
// 只是修改 BaseValue,通常用于非 GE 导致的直接修改
void SetBaseDamage(float NewVal)
{
BaseDamage.SetBaseValue(NewVal);
BaseDamage.SetCurrentValue(NewVal);
}
// 4. 初始化数值(Init)
// 专门用于构造函数中赋初值
void InitBaseDamage(float NewVal)
{
BaseDamage.SetBaseValue(NewVal);
BaseDamage.SetCurrentValue(NewVal);
}
private:
UPROPERTY(BlueprintReadOnly, ReplicatedUsing = OnRep_BaseDamage, Category = "EqZero|Combat", Meta = (AllowPrivateAccess = true))
FGameplayAttributeData BaseDamage;
所以 UEqZeroCombatSet::GetBaseDamageAttribute() 是一个 FGameplayAttribute
加起来 BaseDamageDef 是对于这个attr的 FGameplayEffectAttributeCaptureDefinition 包装
FAggregatorEvaluateParameters EvaluateParameters;
EvaluateParameters.SourceTags = SourceTags;
EvaluateParameters.TargetTags = TargetTags;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BaseDamageDef, EvaluateParameters, BaseDamage);
第二个参数是一个有着Source和Target身上所有gameplay tag的容器,第三个是输出。
bool FGameplayEffectCustomExecutionParameters::AttemptCalculateCapturedAttributeMagnitude(const FGameplayEffectAttributeCaptureDefinition& InCaptureDef, const FAggregatorEvaluateParameters& InEvalParams, OUT float& OutMagnitude) const
{
check(OwningSpec);
const FAggregator* CalcAgg = ScopedModifierAggregators.Find(InCaptureDef);
if (CalcAgg)
{
OutMagnitude = CalcAgg->Evaluate(InEvalParams);
return true;
}
// 。。。
}
他成功走进了第一个IF,
我们需要知道 ScopedModifierAggregators 的数据是什么时候进来的
FGameplayEffectCustomExecutionParameters的构造函数,发现眼熟
不就exec前面一点吗
FGameplayEffectCustomExecutionParameters ExecutionParams(SpecToUse, CurExecDef.CalculationModifiers, Owner, CurExecDef.PassedInTags, PredictionKey);
FGameplayEffectCustomExecutionOutput ExecutionOutput;
ExecCDO->Execute(ExecutionParams, ExecutionOutput);
FGameplayEffectCustomExecutionParameters::FGameplayEffectCustomExecutionParameters(FGameplayEffectSpec& InOwningSpec, const TArray<FGameplayEffectExecutionScopedModifierInfo>& InScopedMods, UAbilitySystemComponent* InTargetAbilityComponent, const FGameplayTagContainer& InPassedInTags, const FPredictionKey& InPredictionKey)
: OwningSpec(&InOwningSpec)
, TargetAbilitySystemComponent(InTargetAbilityComponent)
, PassedInTags(InPassedInTags)
, PredictionKey(InPredictionKey)
{
check(InOwningSpec.Def);
FActiveGameplayEffectHandle ModifierHandle = FActiveGameplayEffectHandle::GenerateNewHandle(InTargetAbilityComponent);
for (const FGameplayEffectExecutionScopedModifierInfo& CurScopedMod : InScopedMods)
{
FAggregator* ScopedAggregator = nullptr;
// ...
}
}
InScopedMods 是啥
for (const FGameplayEffectExecutionDefinition& CurExecDef : SpecToUse.Def->Executions)
{
if (CurExecDef.CalculationClass)
{
FGameplayEffectCustomExecutionParameters ExecutionParams(SpecToUse, CurExecDef.CalculationModifiers, Owner, CurExecDef.PassedInTags, PredictionKey);
}
}
SpecToUse.Def->Executions
SpecToUse 是 GE Spec
Def 是 GameplayEffect
/** Array of executions that will affect the target of this effect */
// 会影响此效果目标的执行数组
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = GameplayEffect)
TArray<FGameplayEffectExecutionDefinition> Executions;
看半天。。。
FGameplayEffectExecutionDefinition 不就是GE里面的这个配置嘛

那么
FGameplayEffectCustomExecutionParameters::FGameplayEffectCustomExecutionParameters(FGameplayEffectSpec& InOwningSpec, const TArray<FGameplayEffectExecutionScopedModifierInfo>& InScopedMods, UAbilitySystemComponent* InTargetAbilityComponent, const FGameplayTagContainer& InPassedInTags, const FPredictionKey& InPredictionKey)
: OwningSpec(&InOwningSpec)
, TargetAbilitySystemComponent(InTargetAbilityComponent)
, PassedInTags(InPassedInTags)
, PredictionKey(InPredictionKey)
{
check(InOwningSpec.Def);
FActiveGameplayEffectHandle ModifierHandle = FActiveGameplayEffectHandle::GenerateNewHandle(InTargetAbilityComponent);
// 这个就是GE的 modify 就好理解了
for (const FGameplayEffectExecutionScopedModifierInfo& CurScopedMod : InScopedMods)
{
FAggregator* ScopedAggregator = nullptr;
// 不管咋样最终就是 ScopedModifierAggregators 加进去东西了
}
}
float BaseDamage = 0.0f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BaseDamageDef, EvaluateParameters, BaseDamage);
bool FGameplayEffectCustomExecutionParameters::AttemptCalculateCapturedAttributeMagnitude(const FGameplayEffectAttributeCaptureDefinition& InCaptureDef, const FAggregatorEvaluateParameters& InEvalParams, OUT float& OutMagnitude) const
{
check(OwningSpec);
const FAggregator* CalcAgg = ScopedModifierAggregators.Find(InCaptureDef);
if (CalcAgg)
{
OutMagnitude = CalcAgg->Evaluate(InEvalParams);
return true;
}
// ...
}
拿最后就是把GE那个数字加到这上面了
另外 FAggregator 是啥,
1. 什么是 FAggregator?
想象你的角色有一个属性 AttackPower (BaseValue = 100)。
你在游戏中可能同时挂着好几个 BUFF:
被动技能:攻击力 +10(GE_Passive)
手里拿着剑:攻击力 +50(GE_Weapon)
吃了狂暴药水:攻击力 x 1.5(GE_Potion)
中了虚弱诅咒:攻击力 x 0.5(GE_Curse)
FAggregator 就是那个负责维护这张清单,并算出最终结果 (100 + 10 + 50) * 1.5 * 0.5 = 120 的组件。
它把所有修改(Modifier)按类型分类存放在不同的“频道”里:
Additive 频道:+10, +50
Multiplicitive 频道:x1.5, x0.5
Override 频道:(如果有的话,直接覆盖数值)
具体是怎么加的呢?
float FAggregator::Evaluate(const FAggregatorEvaluateParameters& Parameters) const
{
EvaluateQualificationForAllMods(Parameters);
return ModChannels.EvaluateWithBase(BaseValue, Parameters);
}
InlineBaseValue 就是我们GE配置的 30,Parameters 里面是我们类构造函数的6
float FAggregatorModChannelContainer::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const
{
float ComputedValue = InlineBaseValue;
for (auto& ChannelEntry : ModChannelsMap)
{
const FAggregatorModChannel& CurChannel = ChannelEntry.Value;
ComputedValue = CurChannel.EvaluateWithBase(ComputedValue, Parameters);
}
return ComputedValue;
}
CurChannel.EvaluateWithBase(ComputedValue, Parameters);
这里面很显然就是具体的运算了
float FAggregatorModChannel::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const
{
for (const FAggregatorMod& Mod : Mods[EGameplayModOp::Override])
{
if (Mod.Qualifies())
{
return Mod.EvaluatedMagnitude;
}
}
float Additive = SumMods(Mods[EGameplayModOp::Additive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Additive), Parameters);
float Multiplicitive = SumMods(Mods[EGameplayModOp::Multiplicitive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Multiplicitive), Parameters);
float Division = SumMods(Mods[EGameplayModOp::Division], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Division), Parameters);
float FinalAdd = SumMods(Mods[EGameplayModOp::AddFinal], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::AddFinal), Parameters);
float CompoundMultiply = UE::AbilitySystem::Private::MultiplyMods(Mods[EGameplayModOp::MultiplyCompound]);
if (FMath::IsNearlyZero(Division))
{
ABILITY_LOG(Warning, TEXT("Division summation was 0.0f in FAggregatorModChannel."));
Division = 1.f;
}
return ((InlineBaseValue + Additive) * Multiplicitive / Division * CompoundMultiply) + FinalAdd;
}
到此我们又有点小以为,这个的 FAggregator 是如何维护的。