news 2026/4/23 22:14:11

49. UE5 GAS RPG 利用Execution Calculations实现动态伤害公式与暴击机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
49. UE5 GAS RPG 利用Execution Calculations实现动态伤害公式与暴击机制

1. Execution Calculations基础概念

在UE5的Gameplay Ability System(GAS)中,Execution Calculations(执行计算)是一个强大的工具,它允许开发者在Gameplay Effect执行期间进行复杂的自定义计算。想象一下你正在设计一个RPG游戏的战斗系统,每次攻击造成的伤害不仅取决于攻击力,还要考虑目标的防御、暴击概率、护甲穿透等多项因素 - 这正是Execution Calculations大显身手的地方。

与ModifierMagnitudeCalculation(MMC)不同,Execution Calculations可以同时修改多个属性。比如在一次攻击中,你不仅可以计算伤害值,还能同时触发暴击效果、减少目标护甲值、施加debuff状态等。这种灵活性让复杂的战斗公式变得容易实现。

我在实际项目中发现,Execution Calculations特别适合处理以下场景:

  • 需要同时考虑攻击方和防御方多个属性的伤害计算
  • 带有随机判定的战斗机制(如暴击、格挡)
  • 需要动态调整计算系数的成长系统
  • 涉及多个属性联动的特殊效果

2. 创建基础伤害计算类

2.1 类结构与属性捕获

让我们从创建一个基础的伤害计算类开始。首先在C++中新建一个继承自UGameplayEffectExecutionCalculation的类:

// ExecCalc_Damage.h #pragma once #include "CoreMinimal.h" #include "GameplayEffectExecutionCalculation.h" #include "ExecCalc_Damage.generated.h" UCLASS() class YOURPROJECT_API UExecCalc_Damage : public UGameplayEffectExecutionCalculation { GENERATED_BODY() public: UExecCalc_Damage(); virtual void Execute_Implementation(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const override; };

属性捕获是Execution Calculations的核心机制。我们可以定义一个内部结构体来管理需要捕获的属性:

// 内部结构体,不需要外部访问 struct FDamageStatics { DECLARE_ATTRIBUTE_CAPTUREDEF(Armor); // 护甲属性 DECLARE_ATTRIBUTE_CAPTUREDEF(AttackPower); // 攻击力 FDamageStatics() { // 捕获目标护甲属性 DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, Armor, Target, false); // 捕获攻击方攻击力属性 DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, AttackPower, Source, false); } }; static const FDamageStatics& DamageStatics() { static FDamageStatics DStatics; return DStatics; }

2.2 执行函数实现

Execute_Implementation是实际计算发生的地方。下面是一个基础实现框架:

void UExecCalc_Damage::Execute_Implementation( const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const { // 获取源和目标ASC const UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent(); const UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent(); // 获取角色实例 AActor* SourceActor = SourceASC ? SourceASC->GetAvatarActor() : nullptr; AActor* TargetActor = TargetASC ? TargetASC->GetAvatarActor() : nullptr; // 获取GameplayEffectSpec const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); // 设置评估参数 FAggregatorEvaluateParameters EvaluationParameters; EvaluationParameters.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags(); EvaluationParameters.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags(); // 获取基础伤害值(通过SetByCaller传入) float Damage = Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Damage"))); // 获取护甲和攻击力属性 float TargetArmor = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorDef, EvaluationParameters, TargetArmor); TargetArmor = FMath::Max(0.f, TargetArmor); float SourceAttackPower = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().AttackPowerDef, EvaluationParameters, SourceAttackPower); SourceAttackPower = FMath::Max(0.f, SourceAttackPower); // 基础伤害计算 Damage += SourceAttackPower * 0.5f; // 攻击力贡献 Damage *= (100.f - TargetArmor * 0.3f) / 100.f; // 护甲减伤 // 输出计算结果 const FGameplayModifierEvaluatedData EvaluatedData(UYourAttributeSet::GetIncomingDamageAttribute(), EGameplayModOp::Additive, Damage); OutExecutionOutput.AddOutputModifier(EvaluatedData); }

3. 实现暴击与格挡机制

3.1 暴击系统实现

暴击是RPG游戏的核心机制之一。我们需要在结构体中添加暴击相关属性:

struct FDamageStatics { // ...已有属性... DECLARE_ATTRIBUTE_CAPTUREDEF(CritChance); // 暴击率 DECLARE_ATTRIBUTE_CAPTUREDEF(CritDamage); // 暴击伤害 DECLARE_ATTRIBUTE_CAPTUREDEF(CritResist); // 暴击抵抗 FDamageStatics() { // ...已有捕获... DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, CritChance, Source, false); DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, CritDamage, Source, false); DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, CritResist, Target, false); } };

然后在Execute_Implementation中添加暴击计算逻辑:

// 获取暴击相关属性 float CritChance = 0.f, CritDamage = 0.f, CritResist = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CritChanceDef, EvaluationParameters, CritChance); ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CritDamageDef, EvaluationParameters, CritDamage); ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().CritResistDef, EvaluationParameters, CritResist); // 计算实际暴击率 float EffectiveCritChance = FMath::Clamp(CritChance - CritResist * 0.5f, 0.f, 100.f); bool bCriticalHit = FMath::RandRange(1, 100) <= EffectiveCritChance; // 应用暴击伤害 if(bCriticalHit) { float CritMultiplier = 2.0f + CritDamage / 100.f; // 基础2倍+额外暴击伤害 Damage *= CritMultiplier; // 可以在这里触发暴击特效或音效 // ... }

3.2 格挡机制实现

格挡是另一个常见的防御机制。添加格挡相关属性:

struct FDamageStatics { // ...已有属性... DECLARE_ATTRIBUTE_CAPTUREDEF(BlockChance); // 格挡率 DECLARE_ATTRIBUTE_CAPTUREDEF(BlockEffectiveness); // 格挡效果 FDamageStatics() { // ...已有捕获... DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, BlockChance, Target, false); DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, BlockEffectiveness, Target, false); } };

格挡计算逻辑:

// 获取格挡属性 float BlockChance = 0.f, BlockEffectiveness = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BlockChanceDef, EvaluationParameters, BlockChance); ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().BlockEffectivenessDef, EvaluationParameters, BlockEffectiveness); // 判断是否触发格挡 bool bBlocked = FMath::RandRange(1, 100) <= BlockChance; if(bBlocked) { // 格挡效果通常在50%-100%之间 float BlockPercent = 0.5f + BlockEffectiveness * 0.005f; Damage *= (1.f - FMath::Clamp(BlockPercent, 0.f, 1.f)); // 触发格挡特效或音效 // ... }

4. 高级伤害公式与动态系数

4.1 护甲穿透系统

护甲穿透让攻击者能够部分忽略目标的护甲值。添加相关属性:

struct FDamageStatics { // ...已有属性... DECLARE_ATTRIBUTE_CAPTUREDEF(ArmorPenetration); // 护甲穿透 FDamageStatics() { // ...已有捕获... DEFINE_ATTRIBUTE_CAPTUREDEF(UYourAttributeSet, ArmorPenetration, Source, false); } };

护甲穿透计算逻辑:

// 获取护甲穿透 float ArmorPenetration = 0.f; ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorPenetrationDef, EvaluationParameters, ArmorPenetration); ArmorPenetration = FMath::Max(0.f, ArmorPenetration); // 计算实际护甲值 float EffectiveArmor = TargetArmor * (100.f - ArmorPenetration) / 100.f; EffectiveArmor = FMath::Max(0.f, EffectiveArmor); // 使用EffectiveArmor代替原始护甲值计算减伤 Damage *= (100.f - EffectiveArmor * 0.3f) / 100.f;

4.2 使用曲线表动态调整系数

为了让游戏数值随等级平衡,我们可以使用曲线表动态调整各种系数:

// 获取角色等级 IYourLevelInterface* SourceLevelInterface = Cast<IYourLevelInterface>(SourceActor); IYourLevelInterface* TargetLevelInterface = Cast<IYourLevelInterface>(TargetActor); int32 SourceLevel = SourceLevelInterface ? SourceLevelInterface->GetCharacterLevel() : 1; int32 TargetLevel = TargetLevelInterface ? TargetLevelInterface->GetCharacterLevel() : 1; // 从曲线表获取动态系数 UDataTable* CoefficientTable = // 获取你的系数表; static const FString ContextString(TEXT("Damage Coefficient Context")); FArmorPenetrationCoefficient* PenetrationCoeff = CoefficientTable->FindRow<FArmorPenetrationCoefficient>(FName(*FString::FromInt(SourceLevel)), ContextString); FArmorCoefficient* ArmorCoeff = CoefficientTable->FindRow<FArmorCoefficient>(FName(*FString::FromInt(TargetLevel)), ContextString); // 应用动态系数 float EffectiveArmor = TargetArmor * (100.f - ArmorPenetration * PenetrationCoeff->Value) / 100.f; Damage *= (100.f - EffectiveArmor * ArmorCoeff->Value) / 100.f;

5. 实战技巧与优化建议

5.1 性能优化

Execution Calculations在服务器上运行,因此性能很重要:

  1. 最小化属性捕获:只捕获真正需要的属性,减少内存访问
  2. 避免复杂计算:将复杂计算预先计算并存储在曲线表中
  3. 使用静态函数:像DamageStatics()这样的辅助函数只初始化一次
  4. 减少动态分配:避免在Execute函数中创建临时对象

5.2 调试技巧

调试伤害计算可能会很棘手,我常用的方法包括:

// 添加调试输出 if(GEngine) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("Damage: %.1f, Crit: %s, Block: %s"), Damage, bCriticalHit ? TEXT("Yes") : TEXT("No"), bBlocked ? TEXT("Yes") : TEXT("No"))); } // 使用UE_LOG UE_LOG(LogTemp, Log, TEXT("Final Damage: %.2f (Base: %.2f, ArmorReduction: %.2f%%)"), Damage, BaseDamage, EffectiveArmor * 0.3f);

5.3 扩展思路

Execution Calculations非常灵活,你还可以实现:

  1. 元素抗性系统:根据不同伤害类型应用不同抗性
  2. 连击加成:基于连续命中次数增加伤害
  3. 背刺/弱点攻击:基于攻击角度增加伤害
  4. 环境加成:考虑地形、天气等因素

我在一个项目中曾实现过基于时间段的伤害加成系统 - 夜晚增加暗影伤害,白天增加光系伤害,只需要在计算时获取游戏世界时间即可。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 5:52:37

智能客服助手实战:基于重排序技术的多查询结果融合策略解析与实现

智能客服助手实战&#xff1a;基于重排序技术的多查询结果融合策略解析与实现 背景痛点&#xff1a;多源查询结果融合的“三座大山” 做智能客服的同学都懂&#xff0c;用户一句“我的订单怎么还没到货&#xff1f;”背后往往要同时查&#xff1a; FAQ 知识库订单图谱工单历史…

作者头像 李华
网站建设 2026/4/22 21:40:08

3个维度提升数据分析效率:ggcor可视化工具实战指南

3个维度提升数据分析效率&#xff1a;ggcor可视化工具实战指南 【免费下载链接】ggcor-1 ggcor备用源&#xff0c;版权归houyunhuang所有&#xff0c;本源仅供应急使用 项目地址: https://gitcode.com/gh_mirrors/gg/ggcor-1 在数据驱动决策的时代&#xff0c;变量间关联…

作者头像 李华
网站建设 2026/4/19 15:07:26

3个革命性的AI预测模型:加密货币市场动态决策实战指南

3个革命性的AI预测模型&#xff1a;加密货币市场动态决策实战指南 【免费下载链接】Kronos Kronos: A Foundation Model for the Language of Financial Markets 项目地址: https://gitcode.com/GitHub_Trending/kronos14/Kronos 在加密货币市场的剧烈波动中&#xff0c…

作者头像 李华
网站建设 2026/4/17 19:20:37

ChatTTS 本地化部署实战:从模型加载到 API 封装的最佳实践

ChatTTS 本地化部署实战&#xff1a;从模型加载到 API 封装的最佳实践 把 3.9 GB 的原始 checkpoint 直接塞进内存&#xff0c;笔记本风扇瞬间起飞&#xff1b;长文本一口气推给模型&#xff0c;线程直接卡死——如果你也踩过这两个坑&#xff0c;下面的踩坑记录或许能帮你把风…

作者头像 李华
网站建设 2026/4/18 7:10:28

无代码自动化工具实现高效数据提取:从入门到精通

无代码自动化工具实现高效数据提取&#xff1a;从入门到精通 【免费下载链接】automa 项目地址: https://gitcode.com/gh_mirrors/aut/automa 在当今数据驱动的时代&#xff0c;网页数据提取已成为信息收集和业务分析的关键环节。传统的手动复制粘贴不仅效率低下&#…

作者头像 李华