Modifier 的进阶:MMC 与 ExecutionCalculation

固定数字与曲线能满足 80% 需求,"伤害 = 攻击力 × 技能倍率 - 护甲 × (1-穿甲)"这类公式需要代码参与。GAS 提供两个入口。

Modifier Magnitude Calculation(MMC)

针对单个 Modifier 的计算类:

UCLASS()
class UMMC_Damage : public UGameplayModifierMagnitudeCalculation
{
    GENERATED_BODY()
public:
    UMMC_Damage();
    float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
};

float UMMC_Damage::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
    // 取自己的攻击力
    float Attack = 0.f;
    UMyAttributeSet* SrcSet = Spec.GetContext().GetSourceASC()->GetSet<UMyAttributeSet>();
    if (SrcSet) Attack = SrcSet->GetAttack();
    return DamageBase * Attack;   // 基础伤害 × 施法者攻击力
}

GE 的 Modifier 处选 Attribute-Based → Calculation Class,即可调用这个 MMC。它轻量、可缓存(GAS 会缓存计算结果)。

什么时候用 MMC:单个属性的简单派生计算(如"伤害随攻击力走")。

Execution Calculation(EC)

针对整个 GE 的自定义执行——不受 Modifier 列表约束,可一次改多个属性、做复杂逻辑:

UCLASS()
class UEC_Damage : public UGameplayEffectExecutionCalculation
{
    GENERATED_BODY()
public:
    void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& Params,
        FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override;
};

void UEC_Damage::Execute_Implementation(...)
{
    // 取来源与目标属性
    const UMyAttributeSet* Src = Params.GetSourceAttributeSet();
    const UMyAttributeSet* Dst = Params.GetTargetAttributeSet();

    float Damage = Src->GetAttack() * SkillMultiplier - Dst->GetDefense() * (1 - Src->GetArmorPen());

    FGameplayModifierEvaluatedData EvalData(UMyAttributeSet::GetHealthAttribute(), EModOp::Add, -Damage);
    OutExecutionOutput.AddOutput(EvalData);
}

什么时候用 EC:完整伤害公式(暴击、抗性、穿甲、最终减伤)、一次结算多个属性、需要 TargetData 参与的结算。

MMC vs EC 选型

MMC Execution Calculation
粒度 单个 Modifier 整个 GE
缓存 有(缓存计算结果) 无
改多属性 否(只返回一个值) 是
TargetData 可读 可读
典型 派生数值 完整伤害公式

一个工程化建议:伤害公式做成数据驱动的"字段配置"

公式再复杂,也建议把倍率、抗性系数、穿甲系数做成 GE 的曲线或 Set-By-Caller 参数:

Spec.Data->SetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag("Data.SkillMultiplier"), 1.5f);

这样"技能倍率"由技能代码传进来,"抗性系数"由 GE 配置——策划能调的部分仍然在资产里。

本章小结

  • MMC:单 Modifier 派生计算(轻量、有缓存)。
  • EC:整 GE 自定义执行(多属性修改、完整公式)。
  • 公式的可配部分用 Set-By-Caller 参数传入,保持"策划可调"。