1. 项目概述为什么你的RPG需要一个伤害公式编辑器做RPG尤其是带点数值深度的最头疼的莫过于技能伤害计算。今天调个系数明天加个暴击判定后天策划又想把“目标当前生命值百分比”也揉进去。每次改动都得重新编译C或者在一堆蓝图节点里翻找效率低不说还容易出错。如果你正在用Unreal Engine 5的Gameplay Ability SystemGAS这种痛苦会加倍——因为GAS本身虽然强大但它的属性计算GameplayEffectModifierMagnitude配置起来并不直观尤其是面对复杂公式时。这个“伤害公式编辑器”要解决的就是这个核心痛点。它不是一个独立的插件而是一种基于GAS现有机制特别是曲线表和Set by Caller构建的数据驱动设计模式。其目标很简单让策划或你自己能在一个类似Excel表格的界面里可视化地配置、调试和迭代复杂的伤害计算公式而无需程序员介入或重新编译项目。想想看一个火球术的伤害基础值来自技能等级曲线智力属性提供加成目标身上的“易伤”Debuff会放大伤害同时这个伤害还不能超过目标当前生命值的50%……这样的公式如果硬编码后期维护就是噩梦。而通过我们即将搭建的这套系统你可以将这些变量等级、智力、易伤系数、目标当前生命值作为参数在一个集中的数据资产里用数学表达式甚至是蓝图函数链接起来实现真正的“所配即所得”。2. 核心设计思路用数据资产驱动公式计算在深入蓝图和C之前我们必须先理清架构。GAS的计算核心是UGameplayEffect它通过Modifiers来改变Attributes。传统的做法是在Modifier的Magnitude里直接设置一个固定值、引用一个曲线表或者使用Set by Caller传递一个临时值。但这几种方式对于“公式”来说都太单一了。我们的设计思路是将复杂的伤害公式拆解为多个可配置的“计算因子”并通过一个中央控制器来组装和执行这些因子。2.1 架构拆解三层结构整个系统可以分为三层数据层Data Asset这是核心。我们将创建一个继承自UDataAsset的类比如叫UDamageFormulaDataAsset。它里面定义了公式所需的所有“变量”如SkillLevel,CasterIntelligence,TargetCurrentHealthRatio和“计算步骤”。计算步骤可以用一个曲线表UCurveTable来定义X轴是输入参数比如一个综合评分Y轴是输出伤害更灵活的方式是在数据资产里配置一个自定义结构描述如何组合这些变量例如先加法后乘法再钳制。逻辑层GameplayEffect Calculation一个自定义的UGameplayEffect并不直接包含复杂计算而是通过Set by Caller从技能GameplayAbility那里获取一个公式ID或关键参数集合。真正的计算由一个自定义的UGameplayModMagnitudeCalculation简称MMC来完成。这个MMC的CalculateBaseMagnitude_Implementation函数会根据传入的公式ID去查找对应的UDamageFormulaDataAsset并执行其中定义的计算逻辑。传递层GameplayAbility技能在激活时负责收集当前所有的上下文信息施法者等级、属性、目标状态等将这些信息打包成一个FGameplayEffectSpecHandle并通过Set by Caller的方式将公式ID和必要的原始参数设置到EffectSpec上。MMC会读取这些Set by Caller的值作为计算的输入。为什么选择 Set by Caller 而不是直接传参Set by Caller是GAS中一种非常灵活的、在运行时动态传递数值的机制。它允许GameplayAbility在创建GameplayEffectSpec时临时绑定一个值到一个自定义的GameplayTag上。MMC或GameplayEffect的Modifier可以在计算时读取这个Tag对应的值。这样做的好处是解耦技能逻辑不需要知道公式的具体细节它只需要提供公式所需的“原料”如攻击力、法强公式的具体计算方式完全由数据资产定义修改公式只需编辑数据资产技能蓝图无需改动。2.2 技术选型曲线表 vs 自定义结构曲线表适合将单一变量映射为伤害值。例如伤害 查找曲线表(SkillLevel)。对于多变量公式需要预先计算一个“综合指数”作为X轴输入不够直观灵活。自定义结构/数据资产这是更推荐的方式。我们可以在UDamageFormulaDataAsset中定义一个TArrayFFormulaNode每个FFormulaNode代表一个计算操作如Add,Multiply,Clamp和其操作数。操作数可以是常数、属性捕获Attribute Capture、或者另一个Set by Caller参数。这相当于在数据资产里实现了一个简单的表达式解析器灵活性极高。本项目教程将聚焦于结合两者优势的混合模式使用曲线表处理单变量等级成长同时使用Set by Caller传递多变量参数并在MMC中用蓝图或C实现公式的组装逻辑。这种模式在保持配置便捷性的同时提供了足够的扩展能力。3. 实操步骤一创建核心数据资产与结构首先我们需要创建承载公式定义的数据结构。这里以C为例因为数据资产定义在C中更为规范和高效之后在蓝图中派生和配置。3.1 创建公式数据结构C在你的项目源代码目录下创建一个新的C类继承自UDataAsset命名为DamageFormulaData。DamageFormulaData.h 关键代码#pragma once #include CoreMinimal.h #include Engine/DataAsset.h #include Engine/CurveTable.h #include GameplayTagContainer.h #include DamageFormulaData.generated.h // 定义一个参数来源的枚举 UENUM(BlueprintType) enum class EFormulaParameterSource : uint8 { Constant, // 常量值 AttributeCapture, // 从施法者或目标身上捕获的属性 SetByCallerTag, // 通过Set by Caller传递的Tag对应的值 CurveTableLookup // 根据输入值查询曲线表 }; // 描述公式中的一个参数 USTRUCT(BlueprintType) struct FFormulaParameter { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula) EFormulaParameterSource Source EFormulaParameterSource::Constant; // 常量值 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Source EFormulaParameterSource::Constant)) float ConstantValue 0.0f; // 属性捕获需要与MMC中的捕获定义匹配 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Source EFormulaParameterSource::AttributeCapture)) FGameplayTag AttributeTag; // 例如Attribute.Strength // SetByCaller 使用的Tag UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Source EFormulaParameterSource::SetByCallerTag)) FGameplayTag SetByCallerTag; // 例如Damage.PhysicalPower // 曲线表查询用哪个CurveTable以及RowName UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Source EFormulaParameterSource::CurveTableLookup)) TSoftObjectPtrUCurveTable CurveTable; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Source EFormulaParameterSource::CurveTableLookup)) FName CurveRowName; // 曲线表查询的输入值参数可以引用另一个FFormulaParameter UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Source EFormulaParameterSource::CurveTableLookup)) FFormulaParameter CurveInputParameter; }; // 描述一个计算节点简化版目前只实现基础运算 USTRUCT(BlueprintType) struct FFormulaNode { GENERATED_BODY() UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula) FString NodeName; // 节点描述方便阅读 // 操作类型Add, Multiply, Divide, ClampMin, ClampMax, etc. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula) FString Operation; // 输入参数A和B UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula) FFormulaParameter InputA; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula) FFormulaParameter InputB; // 钳制操作的上下限 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Operation \Clamp\ || Operation \ClampMinMax\)) float ClampMin 0.0f; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category Formula, meta (EditCondition Operation \Clamp\ || Operation \ClampMinMax\)) float ClampMax 1.0f; }; UCLASS(BlueprintType) class YOURPROJECT_API UDamageFormulaData : public UDataAsset { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadOnly, Category Damage Formula, meta (TitleProperty NodeName)) TArrayFFormulaNode FormulaSteps; // 公式计算步骤序列 // 一个评估公式的函数可以在MMC中调用 UFUNCTION(BlueprintCallable, Category Damage Formula) float EvaluateFormula(const FGameplayEffectSpec Spec, const FGameplayTagContainer SourceTags) const; };DamageFormulaData.cpp 中实现EvaluateFormula的简化逻辑#include DamageFormulaData.h #include GameplayEffect.h float UDamageFormulaData::EvaluateFormula(const FGameplayEffectSpec Spec, const FGameplayTagContainer SourceTags) const { // 这是一个非常简化的评估过程实际项目需要递归解析参数和节点。 // 这里仅示意流程。 float CurrentValue 0.0f; for (const FFormulaNode Node : FormulaSteps) { float ValA ResolveParameter(Node.InputA, Spec, SourceTags); float ValB ResolveParameter(Node.InputB, Spec, SourceTags); if (Node.Operation TEXT(Add)) { CurrentValue ValA ValB; } else if (Node.Operation TEXT(Multiply)) { CurrentValue ValA * ValB; } // ... 处理其他操作 else if (Node.Operation TEXT(Clamp)) { CurrentValue FMath::Clamp(ValA, Node.ClampMin, Node.ClampMax); } // 将当前结果作为下一个节点的潜在输入需要更复杂的图结构来支持 } return CurrentValue; } // ResolveParameter 函数需要根据Parameter的Source类型从Spec或SourceTags中解析出实际值。 // 这涉及到从Spec的SetByCaller中取值或者进行属性捕获这通常在MMC中做更合适。 // 因此更合理的做法是将DataAsset传到MMC中由MMC负责按步骤计算。注意上述C代码提供了一个高度可配置的结构但在编辑器中配置起来可能比较复杂。对于大多数项目一个更实用的起步方案是在数据资产中只定义公式需要用到的Set by Caller的Tag列表和引用的曲线表具体的公式逻辑写在MMC的蓝图或C中。本教程后续将采用这种更易实现的方案但上述结构为你未来的扩展指明了方向。3.2 创建曲线表数据资产在内容浏览器中右键选择杂项Miscellaneous - 曲线表CurveTable。选择Float曲线命名为DT_SkillDamage。双击打开添加行Row。例如行名Fireball_Base表示火球术基础伤害曲线。在右侧表格中定义X轴如SkillLevel和Y轴Damage的关系。例如(0, 10)(5, 30)(10, 60) 这表示技能等级0时伤害10等级5时伤害30等级10时伤害60。引擎会自动在点之间插值。曲线表是我们实现“等级成长”等单变量映射关系的利器配置直观无需代码。4. 实操步骤二实现自定义ModifierMagnitudeCalculationUGameplayModMagnitudeCalculation是GAS中用于进行复杂幅度计算的组件。我们将创建一个自定义的MMC作为公式执行的核心。4.1 创建自定义MMC蓝图或C这里展示蓝图版本因为它更直观且能与数据资产良好协作。在内容浏览器中右键选择蓝图类Blueprint Class。在搜索框中输入GameplayModMagnitudeCalculation选择它作为父类命名为MMC_DamageFormula。双击打开MMC_DamageFormula。4.2 配置属性捕获在MMC_DamageFormula的类默认值Class Defaults中找到属性捕获定义Attribute Capture Definitions部分。这里定义计算中需要从源Source或目标Target身上捕获的属性。例如捕获定义1属性来源Attribute SourceSource施法者属性Attribute选择你的攻击力属性如Attribute.PhysicalAttack捕获时机Snapshotfalse通常我们使用即时值而非技能释放瞬间的快照值捕获定义2属性来源Target属性Attribute.CurrentHealth捕获时机false4.3 重写CalculateBaseMagnitude函数这是MMC的核心函数。我们需要在蓝图中重写它。在MMC_DamageFormula的图表中右键搜索“Calculate Base Magnitude”选择“实现函数Implement Function”。你会得到一个带有Spec和Attribute输入参数的函数。设计计算流程步骤A获取公式ID或参数。从Spec的Set by Caller参数中获取一个标识公式的Tag如Formula.Type或直接获取所有相关参数。使用Get Set by Caller Magnitude节点输入对应的GameplayTag例如Damage.FormulaID。步骤B查找数据资产。根据公式ID可以是一个硬编码的Map或者从一个DataTable中找到对应的UDamageFormulaDataAsset实例。为了简化我们可以假设公式逻辑直接写在MMC里而通过Set by Caller传递关键变量。步骤C执行计算。我们将实现一个经典公式最终伤害 (基础伤害 攻击力系数 * 施法者攻击力) * (1 暴击伤害加成) * 目标防御减免系数。基础伤害从曲线表DT_SkillDamage中根据SkillLevel通过Set by Caller传递的Damage.SkillLevel查询获得。施法者攻击力通过前面定义的属性捕获获得。攻击力系数、暴击伤害加成、防御减免系数都可以通过Set by Caller传递例如Damage.AttackCoeff,Damage.CritMultiplier,Damage.DefenseReduction。步骤D返回结果。将计算出的最终伤害值作为函数的返回值。蓝图节点示意文字描述关键步骤事件 Calculate Base Magnitude (Spec, Attribute) - 返回浮点数 // 1. 从Set by Caller获取技能等级 变量 SkillLevel Get Set by Caller Magnitude (Tag: Damage.SkillLevel, 默认值: 1.0, 从Spec获取) // 2. 从曲线表获取基础伤害 变量 BaseDamage 应用曲线表行 (曲线表: DT_SkillDamage, 行名: Fireball_Base, 时间: SkillLevel) // 3. 捕获施法者攻击力 变量 CasterAttack GetCapturedAttributeMagnitude (捕获定义: 物理攻击捕获, 从Spec和Attribute计算) // 4. 从Set by Caller获取攻击力系数、暴击倍率等 变量 AttackCoeff Get Set by Caller Magnitude (Tag: Damage.AttackCoeff, 默认值: 1.0) 变量 CritMultiplier Get Set by Caller Magnitude (Tag: Damage.CritMultiplier, 默认值: 1.0) // 非暴击时为1 变量 DefenseFactor Get Set by Caller Magnitude (Tag: Damage.DefenseReduction, 默认值: 1.0) // 1代表无减免 // 5. 执行公式计算 变量 FinalDamage (BaseDamage (AttackCoeff * CasterAttack)) * CritMultiplier * DefenseFactor // 6. 可选钳制伤害例如不能超过目标当前生命值 变量 TargetCurrentHealth GetCapturedAttributeMagnitude (捕获定义: 目标当前生命值捕获) FinalDamage Min (FinalDamage, TargetCurrentHealth * 0.5) // 不超过50% 返回 FinalDamage关键技巧使用“Get Set by Caller Magnitude”节点。这个节点是连接GameplayAbility和MMC的桥梁。你需要为公式中用到的每一个动态参数定义一个唯一的GameplayTag如Damage.SkillLevel。在技能蓝图中你必须确保使用Set by Caller的GameplayTag为这些Tag设置好值否则MMC会使用你提供的默认值。5. 实操步骤三在GameplayAbility中装配与调用现在我们有了计算器MMC和配方曲线表、参数Tag最后一步就是在技能GameplayAbility中“点火做饭”。5.1 创建伤害GameplayEffect创建一个新的GameplayEffect蓝图命名为GE_Damage_Fireball。在其Modifiers数组中添加一个修改器Modifier。属性Attribute选择你的生命值属性如Attribute.Health。修改器运算Modifier OpAdd负值即为伤害。幅度值类型Magnitude Calculation Type选择“自定义计算类Custom Calculation Class”。自定义计算类Custom Calculation Class选择我们刚才创建的MMC_DamageFormula。关键一步在Set by Caller部分你不需要在这里直接设置值。这里的GameplayTag定义是告诉GAS“这个Modifier期望从GameplayAbility那里获取这些Tag对应的值”。你可以留空但更规范的做法是添加预期的Tag如Damage.SkillLevel,Damage.AttackCoeff等这有助于文档化和排查问题。5.2 在技能蓝图中设置Set by Caller值在你的火球术GameplayAbility蓝图中通常在ActivateAbility事件中创建MakeGameplayEffectSpec节点指向GE_Damage_Fireball。使用Set Set by Caller Float节点或Set Set by Caller Magnitude来为EffectSpec绑定具体的值。Target: 上一步创建的EffectSpec。Gameplay Tag: 例如Damage.SkillLevel。Magnitude: 可以从技能等级组件、角色状态等获取例如一个Get SkillLevel函数返回的整数值需转换为浮点数。重复步骤2设置所有公式需要的参数Damage.AttackCoeff: 例如0.8表示攻击力贡献80%。Damage.CritMultiplier: 判断是否暴击如果是则设为2.0否则为1.0。Damage.DefenseReduction: 可以根据目标的防御力属性计算出一个0到1之间的系数例如1.0 - TargetDefense * 0.01。最后使用ApplyGameplayEffectSpecToTarget对单个目标或ApplyGameplayEffectSpecToOwner对自身等节点将装配好的EffectSpec应用出去。蓝图流程关键节点链事件 ActivateAbility | |-- 变量 EffectSpec Make Gameplay Effect Spec (GE_Damage_Fireball, Level) | |-- Set Set by Caller Float (EffectSpec, Tag: Damage.SkillLevel, Magnitude: 当前技能等级) |-- Set Set by Caller Float (EffectSpec, Tag: Damage.AttackCoeff, Magnitude: 0.8) | |-- 【暴击判断分支】 | |-- 分支 Condition: 是否暴击? | |-- True - Set Set by Caller Float (EffectSpec, Tag: Damage.CritMultiplier, Magnitude: 2.0) | |-- False - Set Set by Caller Float (EffectSpec, Tag: Damage.CritMultiplier, Magnitude: 1.0) | |-- 【计算防御减免】 | |-- 获取目标防御力属性值 | |-- 变量 DefenseReduction 1.0 - (目标防御力 * 0.01) | |-- Set Set by Caller Float (EffectSpec, Tag: Damage.DefenseReduction, Magnitude: DefenseReduction) | |-- Apply Gameplay Effect Spec To Target (EffectSpec, 目标) | |-- EndAbility5.3 创建“伤害公式编辑器”数据资产实例回到我们最初的高级构想为了让策划能编辑我们还需要最后一步创建DamageFormulaData数据资产的实例并进行配置。在内容浏览器中右键选择蓝图类Blueprint Class或其他创建数据资产的选项找到你的DamageFormulaData类创建一个新的实例命名为DA_Formula_Fireball。双击打开你会看到之前在C中定义的FormulaSteps数组。你可以在这里添加节点配置一个完整的公式树。例如节点1基础伤害Operation: CurveTableLookup配置好曲线表和行名CurveInputParameter的Source设为SetByCallerTag并指定Tag为Damage.SkillLevel。节点2攻击力加成Operation: MultiplyInputA的Source设为AttributeCapture施法者攻击力InputB的Source设为SetByCallerTagDamage.AttackCoeff。节点3求和Operation: AddInputA引用节点1的结果InputB引用节点2的结果。节点4暴击Operation: MultiplyInputA引用节点3的结果InputB的Source设为SetByCallerTagDamage.CritMultiplier。节点5防御减免Operation: MultiplyInputA引用节点4的结果InputB的Source设为SetByCallerTagDamage.DefenseReduction。节点6钳制Operation: ClampInputA引用节点5的结果ClampMin设为0ClampMax通过一个参数计算如目标最大生命值的30%。然后你需要修改MMC_DamageFormula让其不再硬编码公式而是读取DA_Formula_Fireball可以通过另一个Set by Caller传递资源指针或名称并调用其EvaluateFormula函数。这样你就实现了一个完全数据驱动的伤害公式系统。6. 调试技巧与常见问题排查搭建这套系统时你肯定会遇到各种问题。以下是一些实战中总结的调试技巧和常见坑点。6.1 调试技巧打印Set by Caller的值在GameplayAbility中设置完所有Set by Caller值后使用Get Set by Caller Magnitude节点读取并打印Print String出来确认传递的值是否正确。在MMC中打印中间计算结果在CalculateBaseMagnitude函数中在每个计算步骤后插入打印节点输出BaseDamage、CasterAttack、CritMultiplier等中间变量。这是定位公式计算错误最直接的方法。使用Gameplay Debugger在编辑器运行时按下“~”键输入showdebug abilitysystem。将鼠标悬停在角色上可以查看其身上的Active Gameplay Effects和Attributes确认伤害效果是否被正确应用以及幅度Magnitude是多少。检查GameplayTag确保Set by Caller使用的GameplayTag如Damage.SkillLevel在项目中被正确定义并且拼写完全一致包括大小写。一个常见的错误是Tag未定义或拼写错误导致MMC读取不到值而使用默认值。6.2 常见问题与解决方案问题现象可能原因排查步骤与解决方案伤害始终为0或默认值1.Set by Caller的Tag未设置或设置失败。2. MMC中的属性捕获失败。3. 曲线表行名错误或查询失败。1. 在Ability中打印所有Set by Caller的值。2. 在MMC中打印捕获到的属性值。3. 检查曲线表资产引用和行名尝试直接给一个固定值测试。伤害值异常大或小1. 公式计算错误如乘除法弄反。2. 属性捕获源Source/Target选错。3.Set by Caller传递了错误的值如把等级传成了攻击力。1. 在MMC中逐步打印每个计算步骤的结果。2. 核对MMC中每个属性捕获的定义是捕获Source还是Target。3. 核对Ability中每个Set Set by Caller节点对应的Tag和值。应用GameplayEffect时崩溃1.GameplayEffectSpec创建失败如GE类为空。2. MMC类引用错误或未编译。3. 数据资产如曲线表引用无效。1. 检查MakeGameplayEffectSpec节点的Class输入是否有效。2. 重新编译包含MMC或数据资产C代码的项目。3. 检查内容浏览器中相关资产是否有红色错误标识。修改数据资产后伤害不变1. 数据资产没有被正确引用或加载。2. MMC缓存了旧的数据。1. 确保MMC中获取数据资产的逻辑正确可以使用LoadObject异步加载或直接引用软指针TSoftObjectPtr。2. 重启编辑器或游戏清除内存缓存。多人游戏中伤害不一致1. 计算中使用了本地变量而非权威属性。2.Set by Caller的值在客户端和服务器上不同步。1. 确保所有输入参数如属性值都来自GAS权威的属性系统。2.关键Set by Caller的操作必须在服务器执行的Ability路径上。确保Set Set by Caller的节点在服务器端触发。客户端预测部分可以设置但服务器是权威。6.3 性能优化注意事项避免在MMC中进行复杂查找如果EvaluateFormula函数需要根据ID从DataTable或Map中查找数据资产尽量将这个查找过程提前例如在技能加载时并将结果缓存起来避免每帧或每次伤害计算都进行查找。简化公式复杂度虽然系统支持复杂公式但过于复杂的节点计算尤其是涉及循环或递归会在高频伤害如DOT时带来性能压力。对于极其复杂的公式考虑将其预计算为几个核心系数通过Set by Caller传递。谨慎使用属性捕获Snapshot设置为true的属性捕获性能开销较小但值是固定的。false的捕获每次计算都会查询当前属性值开销相对大。根据业务需求选择。7. 扩展思路从编辑器到生态系统实现基础版本后你可以考虑以下扩展方向让它真正成为一个强大的“编辑器”和生态系统可视化公式编辑界面利用Unreal的Details面板自定义和Slate编程为UDamageFormulaDataAsset创建一个自定义的编辑器工具FAssetEditorToolkit。在这个工具中策划可以通过拖拽节点、连线的方式类似蓝图来编辑公式计算图远比在数组里配置结构体直观。公式模拟与测试在编辑器中可以创建一个测试面板输入模拟的施法者属性、目标属性、技能等级等参数实时运行公式并输出预测伤害值实现“所见即所得”的调试。公式版本管理与继承为公式数据资产添加版本号或继承机制。可以创建一个基础公式如物理伤害公式然后派生出具体的技能公式如重击公式、旋风斩公式后者只覆盖需要修改的部分参数或节点。与技能编辑器集成将伤害公式编辑器作为你项目技能编辑器GameplayAbility编辑器的一个子面板或标签页。在配置技能时可以直接选择或编辑其关联的伤害公式资产形成工作流闭环。导出与数据分析将配置好的公式逻辑导出为JSON或纯数学表达式供服务器如果使用独立服务器或其他数据分析工具使用确保客户端和服务器端计算逻辑的一致性。这套基于GAS曲线表和Set by Caller的伤害公式系统其核心思想是“将易变的逻辑数据化”。它一开始可能需要一些搭建成本但一旦运转起来对于任何需要频繁调整数值的RPG或动作游戏项目来说带来的迭代效率提升是巨大的。它把数值平衡从程序员的编译等待中解放出来交到了策划和设计师手中这正是现代游戏开发数据驱动理念的一个具体实践。