LyraLog12 伤害流程断点

断几个地方

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 是如何维护的。

上一篇
下一篇