news 2026/3/28 19:46:39

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/3/25 8:00:26

下一代开源操作系统:重构桌面计算体验的革命

下一代开源操作系统&#xff1a;重构桌面计算体验的革命 【免费下载链接】Atlas &#x1f680; An open and lightweight modification to Windows, designed to optimize performance, privacy and security. 项目地址: https://gitcode.com/GitHub_Trending/atlas1/Atlas …

作者头像 李华
网站建设 2026/3/21 9:09:00

计算机毕业设计之家:基于微服务架构的毕设项目实战与避坑指南

计算机毕业设计之家&#xff1a;基于微服务架构的毕设项目实战与避坑指南 一、背景痛点&#xff1a;毕设项目为何总被导师打回&#xff1f; 单体架构臃肿 传统“大一统”Spring MVC 项目把所有功能塞进一个模块&#xff0c;随着需求迭代&#xff0c;代码膨胀、耦合度飙升&…

作者头像 李华
网站建设 2026/3/24 17:14:16

5种终极方案:让开发者突破AI编程助手限制

5种终极方案&#xff1a;让开发者突破AI编程助手限制 【免费下载链接】go-cursor-help 解决Cursor在免费订阅期间出现以下提示的问题: Youve reached your trial request limit. / Too many free trial accounts used on this machine. Please upgrade to pro. We have this li…

作者头像 李华
网站建设 2026/3/27 10:19:23

PaddleOCR推出韩语识别模型:korean_PP-OCRv5_mobile_rec准确率达88%

PaddleOCR推出韩语识别模型&#xff1a;korean_PP-OCRv5_mobile_rec准确率达88% 【免费下载链接】korean_PP-OCRv5_mobile_rec 项目地址: https://ai.gitcode.com/paddlepaddle/korean_PP-OCRv5_mobile_rec 百度飞桨旗下OCR开源项目PaddleOCR正式发布针对韩语优化的文本…

作者头像 李华
网站建设 2026/3/27 21:12:47

零代码企业级在线考试平台:轻量化部署与多终端解决方案

零代码企业级在线考试平台&#xff1a;轻量化部署与多终端解决方案 【免费下载链接】xzs-mysql 学之思开源考试系统是一款 java vue 的前后端分离的考试系统。主要优点是开发、部署简单快捷、界面设计友好、代码结构清晰。支持web端和微信小程序&#xff0c;能覆盖到pc机和手机…

作者头像 李华
网站建设 2026/3/28 11:38:26

如何用5个秘诀解决FreeCAD插件管理难题?

如何用5个秘诀解决FreeCAD插件管理难题&#xff1f; 【免费下载链接】FreeCAD This is the official source code of FreeCAD, a free and opensource multiplatform 3D parametric modeler. 项目地址: https://gitcode.com/GitHub_Trending/fr/freecad FreeCAD插件管理…

作者头像 李华