news 2026/4/18 11:01:54

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/18 8:33:50

3步实现数字音频完美归档:foobox-cn的高保真音频提取终极解决方案

3步实现数字音频完美归档&#xff1a;foobox-cn的高保真音频提取终极解决方案 【免费下载链接】foobox-cn DUI 配置 for foobar2000 项目地址: https://gitcode.com/GitHub_Trending/fo/foobox-cn 当收藏家老李第三次擦拭那张1985年的《崔健-新长征路上的摇滚》CD时&…

作者头像 李华
网站建设 2026/4/18 8:48:21

窗口隐身术:让Windows桌面秒变清爽的系统托盘管理指南

窗口隐身术&#xff1a;让Windows桌面秒变清爽的系统托盘管理指南 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 痛点分析&#xff1a;被窗口淹没的数字桌面 想象你的电…

作者头像 李华
网站建设 2026/3/30 11:54:08

如何轻松掌握Mootdx:通达信数据解析实战指南

如何轻松掌握Mootdx&#xff1a;通达信数据解析实战指南 【免费下载链接】mootdx 通达信数据读取的一个简便使用封装 项目地址: https://gitcode.com/GitHub_Trending/mo/mootdx Mootdx是一个专为通达信数据解析设计的Python工具包&#xff0c;能够帮助你轻松读取通达信…

作者头像 李华