做战斗模拟器的第一张表,我建议从升级经验表开始。原因很实在:一张管着“多少经验升一级”的表,是整套战斗模拟器里所有数值的地基——怪物给多少经验、玩家多久能到满级、某把武器该在多少级掉落,全都挂在这张表上。它一变,后面的属性成长表、怪物强度表、掉落表全得跟着重算。用Excel做这件事,好处是改一个参数就能立刻看到整条曲线的变化,不用等编译、不用改代码,试错成本几乎为零。这篇内容面向想自己动手搭战斗数值原型的人,不管你是刚接触数值设计的新手,还是想找个轻量工具快速验证手感的老手,都能照着做出一张能用的表。
1. 战斗模拟器的第一张表为什么是升级经验表
1.1 模拟器的本质是数字按规则互相咬合
很多人对“战斗模拟器”这四个字的想象是:输入攻击力、防御力、血量,输出一场战斗的结果。这个理解只对了一半。真正的战斗模拟器是一整套互相咬合的数值系统:等级决定属性,属性决定伤害,伤害决定击杀速度,击杀速度决定单位时间收益,收益又决定升级速度,升级速度最后回过头来影响等级。这是一个闭环,而经验表就是这个闭环里唯一一个“由玩家行为驱动”的环节。
把这个闭环拆开看,你会发现经验表是少数几个能独立出来先做、又不依赖其他表的部分。属性成长表需要你先定好等级上限和成长曲线,怪物表需要你先定好玩家在某个等级应该有多强,只有经验表可以脱离它们单独存在——它只回答一个问题:从 L 级到 L+1 级,需要多少经验。这个问题一旦有了答案,你就有了“时间轴”,后面所有数值都可以挂在时间轴上讨论。
我见过不少人在引擎里写脚本做这件事,结果调一次参数要改代码、重跑、看日志,一轮下来十几分钟。Excel 里改一个单元格,整张表连带图表一秒刷新,这个反馈速度对“调手感”阶段是决定性的。手感这种东西,靠脑补是补不出来的,必须看到曲线才作数。
注意:这里说的“先做经验表”,前提是你的模拟器以等级成长为骨架。如果做的是纯回合制对战、玩家不升级,那第一张表应该是别的,别硬套。
1.2 经验表在模拟器里承担的三个角色
第一层角色是时间刻度。经验值本质上是把“玩家游玩时长”翻译成数字的汇率。你希望玩家 20 小时到满级,满级总经验 80 万,那平均每小时就是 4 万经验,这个数字回头会约束怪物经验产出、任务奖励、副本次数。没有经验表,你连“这个怪该给多少经验”都没法回答。
第二层角色是节奏控制器。经验曲线陡不陡,直接决定玩家在哪个阶段会感到吃力。线性曲线意味着每级耗时差不多,指数曲线意味着后期每一级都是煎熬,幂函数夹在中间。这几种手感差异巨大,而差异全部体现在一条公式上。
第三层角色是跨表接口。属性成长表要按等级查属性,怪物表要按等级查强度,掉落表要按等级分档,它们都需要一个统一的等级主键。经验表正好提供了这个主键,还顺带提供了“玩家此刻大概在什么进度”的百分比信息。后面做难度曲线的时候,你会频繁用到“到达 30 级所需累计经验占总经验的百分之多少”这个数。
1.3 Excel 做数值原型的优势与边界
优势很明确:公式可读、改参数即时反馈、图表现成、能导出 CSV 给程序读、非程序员也能打开改。做数值原型最怕的是“只有写代码的人能改”,一旦策划想试另一个方案就得排队等开发。Excel 把这个环节的门槛降到几乎为零。
边界也要说清楚。Excel 不适合做几千次战斗的批量模拟,那是脚本的活;它也不适合做多表联动的复杂查询,数据量上万行之后 VLOOKUP 会开始拖慢。它最合适的定位是:把公式和参数定下来,验证手感,最后把结论搬到代码里。所以做这张表的时候要有个意识——表是给人看的,不是给机器跑的,公式写清楚比写得短更重要。
我在实际项目里的做法是:Excel 里保留一份“设计稿”版本,注释齐全、颜色分区、图表齐全;程序里读的是从这张表导出的 CSV。两边保持同一套参数,改设计稿的时候同步改配置。这样既不耽误调参,也不会出现“表里一套、代码里一套”的经典事故。
2. 经验曲线的选型:先定手感,再定公式
2.1 四种曲线的横向对比
打开表之前,先想清楚你想要什么手感。常见的四种:
| 曲线类型 | 公式形态 | 特征 | 适合的场景 |
|---|---|---|---|
| 线性 | EXP = A + B*(L-1) | 每级增量恒定,耗时均匀 | 短周期、关卡制、等级少 |
| 幂函数 | EXP = K * L^B | 绝对值加速、增长率递减 | 大多数 RPG 的主力选择 |
| 指数 | EXP = K * r^(L-1) | 每级按固定比例膨胀 | 等级少、满级门槛极高 |
| 分段 | 前段手填、后段公式 | 每个阶段独立控制 | 有明确新手期/中期/后期划分 |
线性曲线的问题在于“没有重量感”。玩家从 1 级到 60 级,每一级花的时间差不多,后期毫无压力,等级就变成了一个纯数字。指数曲线的另一个极端是后期爆炸,如果等级数多,60 级可能需要几百亿经验,数值会溢出到没法看。
幂函数之所以成为主流,是因为它的数学性质刚好匹配玩家的心理预期:每级需要的经验绝对值在涨,但涨幅的百分比在缩小。也就是说,玩家感觉到“越来越难”,但这个难度增长是收敛的,不会失控。这个特性后面会用具体数字展示。
2.2 用“满级单级需求”倒推参数
调参最容易犯的错是从公式出发,先定个L^1.5再说。正确的顺序是反过来的:先定两个锚点,再解出参数。
锚点一:1 级升 2 级需要多少经验。这个数决定新手期的第一印象,通常取一个整数,比如 100。 锚点二:满级前一级升满级需要多少经验。这个数决定后期的沉重感,比如 30000。
假设用幂函数EXP = K * L^B,等级上限 60:
K * 1^B = 100 → K = 100 K * 59^B = 30000 → 59^B = 300 B = ln(300) / ln(59) = 5.7038 / 4.0775 = 1.399取B = 1.4,K = 100,公式就是EXP = ROUND(100 * L^1.4, 0)。这个取整不是为了好看,是因为经验值必须是整数,小数经验在界面上没法显示,也会在累计时产生误差。
如果你希望后期更陡一点,把满级锚点改成 50000,重新解一遍:B = ln(500)/ln(59) = 6.2146/4.0775 = 1.524。B 每涨 0.1,后期的压力就会有明显变化。这就是为什么先定锚点再解参数——锚点是你真正关心的东西,B 只是中间产物。
2.3 曲线自检:三个必须回答的问题
参数算出来别急着往下做,先自检三个问题。
第一,前 10 级累计经验占比是多少。这个数字决定新手期长度。用积分粗估:∫1^10 100x^1.4 dx = 100*(10^2.4 - 1)/2.4 ≈ 10400,而满级总经验约 77 万,占比 1.35%。这个比例说明前 10 级只占整个旅程的百分之一多一点,新手期非常短,玩家会很快进入正题。如果你希望新手期更从容,这个比例应该拉到 3%~5%。
第二,50 级到 60 级占了多少。同样估算:前 50 级累计约 49.9 万,占总量的 64.7%。也就是说最后 10 级吃掉了 35% 的经验。这个分布意味着玩家在 50 级之后会明显感到“路还很长”。如果这个数字超过 45%,后期会显得拖沓;低于 25%,满级来得太容易。
第三,相邻两级的经验比例是多少。59 级需要 30110,58 级需要 29430,比值 1.023,也就是每级只涨 2.3%。而指数曲线100*1.1^(L-1)每级恒定涨 10%。这两种手感完全不同:幂函数的“每级增幅”在视觉上不明显,玩家感受到的是持续的小幅加码;指数函数则是每隔几级就有一次明显的“跳档”。
提示:这三个自检数字建议做成表头的固定单元格,改参数的时候盯着它们看,比盯着公式直观得多。
3. 表格骨架搭建:参数、计算、展示分三层
3.1 三层结构与版式规划
一张能长期维护的表,结构必须分区。我的习惯是分三层,从上到下:
参数层(第 1~5 行):只放可调的输入,比如基数 K、指数 B、等级上限、取整方式。这一层用浅黄底色标出来,提醒自己“只能改这里”。
计算层(第 7 行往下):等级、单级经验、累计经验、占比。全部由公式生成,不允许手填(分段曲线的手填部分除外,那部分单独标色)。
展示层(右侧或另一个 sheet):图表、关键指标汇总、反查工具。这一层只读不写。
这个分区的意义在于防错。做数值表最怕的就是“公式里混着手填数字”,改一次参数发现某几行没跟着变,排查半天。参数集中在一处,公式全部引用命名区域,改参数就是改一个格子,整表连动,不会遗漏。
具体布局:
| 单元格 | 内容 | 说明 |
|---|---|---|
| B1 | 基数 K = 100 | 手动输入 |
| B2 | 指数 B = 1.4 | 手动输入 |
| B3 | 等级上限 = 60 | 手动输入 |
| B4 | 满级总经验 | 公式汇总 |
| A7:A66 | 等级 1~60 | 公式生成 |
| B7:B66 | 单级升级所需经验 | 公式生成 |
| C7:C66 | 到达本级累计经验 | 公式生成 |
| D7:D66 | 累计占比 | 公式生成 |
等级列不用手敲 60 行。A7 填 1,A8 填=A7+1,往下拖到 60 行结束。这个方法比填充序列更稳,因为改了上限之后只需要调整拖拽范围,不会出现“序列步长记错”的问题。
3.2 命名区域:让公式能读懂
=ROUND($B$1*A7^$B$2,0)这个公式,三个月后你自己都要想一下$B$1是什么。换成=ROUND(BaseK*A7^CurveB,0)就一目了然。
设置方法:选中 B1,在左上角的名称框里输入BaseK回车;B2 命名为CurveB;B3 命名为MaxLv。之后所有公式都用这三个名字,不再出现绝对引用。
命名区域的好处不只是可读。它的引用是绝对的,复制公式到任何位置都不会错位,这刚好避开了“填充时忘加美元符号”这个高频事故。另外,如果你以后要把参数挪到另一个 sheet,命名区域会自动跟随,公式不用改。
命名有几个约定俗成的规矩:名字里不能有空格,用下划线或驼峰都行;不要用A1、R1C1这种看起来像单元格引用的名字;不要和函数名撞车,比如不能叫SUM。我一般统一加个前缀区分用途,参数用P_,中间量用M_,虽然啰嗦但搜索的时候好找。
3.3 字段设计与约定
字段设计里最容易含糊的是“累计经验”到底指什么。有两种常见约定:
- 约定 A:C 列表示“从 1 级升到当前等级所需的总经验”,1 级时为 0。
- 约定 B:C 列表示“当前等级升到下一级之后的总经验”。
这两种在数值上差一行,但混淆之后会导致反查等级时错一位。我推荐约定 A,因为“到达某等级需要多少经验”是更自然的问法,也方便直接和玩家界面上的进度条对应。
按约定 A,C7(1 级)手动填 0,C8 开始写=SUM($B$7:B7)。注意这里的绝对引用:$B$7锁死起点,B7相对引用跟着行走。拖到第 60 行时公式变成=SUM($B$7:B65),正好把前面所有单级经验加起来。
还有一个字段值得加:单级经验占上一级的比例,E 列,公式=B8/B7。这一列的意义是让你一眼看出曲线在哪个位置“变陡”了。幂函数这一列会缓慢下降,指数函数会保持恒定,分段曲线则会在接缝处出现跳变。做分段设计的时候,这一列是判断接缝是否平滑的主要依据。
4. 公式落地:从单点到整表
4.1 单级经验的公式实现
B7 的公式:
=ROUND(BaseK*A7^CurveB, 0)这里有几个细节值得说。
取整函数选 ROUND 还是 ROUNDUP。ROUND是四舍五入,ROUNDUP是向上取整。用ROUNDUP的好处是保证“每一级至少需要 1 点经验”,不会出现某级需要 0.4 经验、结果显示 0 的情况。但ROUNDUP会让所有值系统性偏大一点点,长期累计会多出几千点。我一般用ROUND,同时在参数区加一个最小值保护:公式改成=MAX(1, ROUND(BaseK*A7^CurveB,0)),既避免 0 值,又不引入系统性偏差。
为什么不在公式里直接算累计。有人喜欢把 C 列写成=SUMPRODUCT(ROUND(BaseK*ROW(INDIRECT("1:"&A7))^CurveB,0))这种一步到位的写法。这种公式看起来很厉害,但有两个致命问题:一是每次重算都要跑一遍数组,60 行还好,几百行就开始卡;二是出错之后完全没法排查,你根本不知道是哪一级算错了。分成单级和累计两列,错了一眼就能定位。
A7 的引用要不要锁。不要锁。A7 是相对引用,往下填充时自动变成 A8、A9,正好对上每一行。而BaseK和CurveB是命名区域,本身就是绝对的,不需要美元符号。
4.2 累计经验的两种写法
第一种是前面说的=SUM($B$7:B7),简单直接。缺点是如果中间某行被删掉,SUM 的范围会自动收缩,但引用它的行不会,容易出现范围错位。而且每一行都要重新求一次和,60 行就是 60 次求和,虽然不慢,但不优雅。
第二种是递推写法:C8 ==C7+B7。这一行只依赖上一行的累计值和本行的单级经验,计算量是常数。缺点是起始行必须手填 0,而且如果中间插入一行,整条链会断。
两种写法我都用过,结论是:等级数在 200 以内用 SUM,超过 200 用递推。原因是 SUM 的可读性明显更好,范围一目了然,排查错误时不容易看花眼。等级数大了之后 SUM 的重复计算才开始成为负担。
还有个折中方案:用 Excel 的“表格”功能(Ctrl+T),把数据区转成结构化表格,累计列用=SUM(INDEX([单级经验],1):[@单级经验])。结构化引用最大的好处是新增行会自动扩展公式,不用手动拖。缺点是公式写法对新手不太友好,而且导出 CSV 的时候表头会带上方括号,需要额外处理。
4.3 分段曲线的实现
如果不想全程用一条公式,分段是更灵活的做法。典型的分段方案:
- 1~10 级:手填固定值,比如 100、150、220、300……用来精细控制新手引导期的节奏。
- 11~40 级:幂函数,主力成长期。
- 41~60 级:换成更陡的幂函数或者指数,制造后期压力。
实现方式有两种。第一种是在 B 列用 IF 判断:
=IF(A7<=10, INDEX($F$7:$F$16, A7), IF(A7<=40, ROUND(BaseK*A7^CurveB,0), ROUND(BaseK2*A7^CurveB2,0)))其中 F7:F16 是手填的前 10 级经验值,BaseK2/CurveB2 是后期参数。这种写法的问题是嵌套层数一多就没法读,而且接缝处的数值可能不连续——第 10 级手填 800,第 11 级公式算出 750,曲线会往回掉,玩家会觉得莫名其妙。
第二种是用查找表 + 分段参数表。单独做一个小表,列出每一段的起始等级、结束等级、基数、指数,然后用MATCH找到当前等级属于哪一段,再用INDEX取出对应参数:
=ROUND(INDEX($K$7:$K$9, MATCH(A7, $I$7:$I$9, 1)) * A7^INDEX($L$7:$L$9, MATCH(A7, $I$7:$I$9, 1)), 0)这个写法长,但结构清晰:参数区就是一个三行的小表,改哪一段改哪一行,不会碰到公式。而且MATCH的近似匹配(第三个参数 1)要求起始等级列升序排列,正好符合分段表的自然顺序。
注意:分段设计一定要做接缝检查。方法是在 E 列算相邻两级的比例,如果某一行突然从 1.05 跳到 1.3 或掉到 0.9,说明接缝没对齐,需要微调手填值或者调整下一段的起始参数。
4.4 反查:给经验值找等级
表做完之后,最常用的功能不是“查某级需要多少经验”,而是反过来——“玩家现在有 15 万经验,他几级了”。这个查询在模拟器里天天要用,写不好会拖慢整张表。
新版本 Excel 用 XLOOKUP:
=XLOOKUP($G$2, $C$7:$C$66, $A$7:$A$66, 0, -1)五个参数分别是:查找值、查找数组(累计经验列)、返回数组(等级列)、找不到时返回 0、匹配模式 -1(精确匹配或下一个较小项)。最后这个-1是关键,它表示“找一个不超过查找值的最大项”,正好对应“经验够到哪一级”。这个模式要求查找数组升序,而累计经验列天然升序,满足条件。
老版本 Excel 没有 XLOOKUP,用 INDEX + MATCH:
=INDEX($A$7:$A$66, MATCH($G$2, $C$7:$C$66, 1))MATCH的第三个参数 1 和 XLOOKUP 的 -1 是一个意思。要注意的是MATCH在查找值小于数组第一个元素时会返回错误,所以外面最好套一层IFERROR。
这里有个容易忽略的坑:如果查找值是经验上限之外的数,比如玩家经验超过了满级总经验,XLOOKUP 会返回最高等级,逻辑上没问题;但如果查找值是负数或者空值,可能返回意外结果。稳妥的做法是在查找值外面套MAX(0, ...),把非法输入挡掉。
5. 让表活起来:校验、格式化与联动
5.1 数据验证与输入约束
参数区只有三个格子,但改错一个整表就废。用数据验证(数据 → 数据验证)把输入范围卡死:
| 参数 | 允许类型 | 范围 | 出错提示 |
|---|---|---|---|
| 基数 K | 小数 | 0.1 ~ 100000 | 基数必须是正数 |
| 指数 B | 小数 | 1.0 ~ 3.0 | 指数超出常规范围,曲线会失控 |
| 等级上限 | 整数 | 1 ~ 500 | 等级上限建议不超过 500 |
指数 B 的范围限制尤其重要。B 小于 1 会得到一条“越来越平”的曲线,后期升级比前期还快,几乎没人这么设计;B 大于 3 会让后期的经验值膨胀到没法看,60 级可能需要几千万。把范围卡在 1.0~3.0,能挡掉绝大多数误操作。
等级上限的验证还要配合一件事:A 列的公式生成范围必须和上限对齐。如果你把上限改成 80,但 A 列只拖到 60 行,那 61~80 级就是空白,图表会断开。解决办法是把 A 列拖到 500 行,然后用条件格式把超过上限的行灰掉,或者用=IF(A7>MaxLv, "", ...)把超出的行隐藏。
5.2 条件格式与图表
条件格式最实用的两个用法。
一是累计经验列加数据条。选中 C7:C66,条件格式 → 数据条 → 渐变色。这样一眼就能看出经验量的分布:前半段全是短条,后半段突然拉长,视觉上直接呈现了曲线的形状。这比看数字快得多。
二是单级经验列加三色色阶。低值绿、中值黄、高值红,能快速定位曲线上变化剧烈的位置。做分段设计时,接缝处的颜色跳变会非常明显。
图表方面,做两个就够了。
第一个是散点图:X 轴等级,Y 轴单级经验,加平滑线。这个图看的是曲线形状,判断是不是你想要的加速感。做完之后右键横轴,把“边界最小值”设成 1 而不是 0,否则 1~5 级会被挤在左边看不清楚。
第二个是累计经验图,同样 X 轴等级、Y 轴累计经验。这个图看的不是形状而是“拐点”,累计曲线在哪一段开始变得几乎垂直,那一段就是玩家的疲劳区。如果垂直段太长(比如占了整个图表宽度的三分之一),说明后期设计过重了。
提示:图表的数据源一定要用命名区域或者结构化引用,不要写死
$A$7:$B$66。等级上限一改,写死的范围不会自动扩展,图表就少了一截。
5.3 与后续模块的接口
经验表做完,下一步是接属性成长表。接口设计上有几个决定要提前定。
等级主键的统一。经验表里的等级是 1~60 的整数,属性表也必须用同一套编号,不能一个从 0 开始一个从 1 开始。这个看起来是废话,但我见过太多项目在这里出问题——经验表的 1 级对应属性表的 0 级,结果所有属性查出来都低了一档。
放置方式的选择。经验表和属性表放同一个 sheet 还是分开?我的建议是分开,各占一个 sheet,用=经验表!A7这种方式跨表引用。同 sheet 的话行数一多,滚动起来很痛苦;而且导出 CSV 的时候,一个 sheet 一个文件,分开更方便程序读取。
导出格式。程序读的那份数据,只需要等级、单级经验、累计经验三列,其他辅助列(占比、比例)都是给人看的,导出前删掉。我一般单独做一个“导出”sheet,用公式把需要的列引过来,导出时只导这一个 sheet,避免把参数区和图表混进去。
预留扩展位。经验表未来可能要支持多职业、多难度、经验加成倍率。这些不是现在做,但字段要留出来。比如在参数区加一个“全局经验倍率”,公式改成=ROUND(BaseK*A7^CurveB*RateMultiplier,0),默认值 1。这个格子平时不用,但需要做活动加成、难度区分的时候,改一个数就搞定,不用回头改公式。
6. 常见问题与排查实录
6.1 取整顺序引发的连锁偏差
这个问题最隐蔽。假设你要算“到达 30 级的总经验”,有两种算法:
算法一:先算每级经验并取整,再求和。=SUM(B7:B36)。 算法二:先算总和再取整。=ROUND(SUMPRODUCT(BaseK*ROW(1:30)^CurveB), 0)。
这两个结果一定不同,差值可能达到几十点。原因是每次取整都会丢掉小数部分,30 次取整丢掉的部分累加起来就是偏差。
哪个对?算法一对。因为玩家的经验值是一级一级加上去的,每次加的都是整数,游戏里不可能出现“累计到 30 级时把历史误差一次性抹平”的操作。所以表里的累计列必须用算法一,任何时候都不要用算法二去“校验”。我见过有人用算法二算出一个数,发现和表里对不上,然后去改公式,越改越乱。
同样的道理适用于反查等级。如果程序里用“累计经验公式”直接算,和 Excel 表里的结果会有微小差异,表现为“玩家经验刚好够升级,但游戏里没升”。解决办法只有一个:程序读表,不要在代码里重算。
6.2 引用与填充的坑
坑一:忘加美元符号。B7 的公式是=ROUND(BaseK*A7^CurveB,0),如果把A7写成$A$7,往下填充时所有行都会引用第一级,整列变成同一个数字。这个错误在公式栏里看不出来,因为第一行是对的,只有往下看才发现全是 100。
坑二:SUM 的起点没锁。C8 应该是=SUM($B$7:B7)。如果写成=SUM(B7:B7),拖到 C9 会变成=SUM(B8:B8),累计列直接变成单级经验的复制。判断方法很简单:看 C 列最后一行,如果不是单调递增的,就是这里错了。
坑三:整列引用拖慢计算。有人图省事写=SUM(B:B),包含表头、参数区和所有空白行。表小的时候没问题,一旦加了图表和条件格式,每次重算都要遍历上百万个单元格,会明显卡顿。养成习惯,范围写到具体行号。
坑四:中文标点。公式里的逗号必须是半角。从网页上复制公式粘贴进 Excel,经常把半角逗号变成全角,Excel 会直接报错或者把它当成文本。排查方法是选中单元格看编辑栏,全角逗号看起来更宽,一眼能认出来。
6.3 问题速查表
| 现象 | 最可能的原因 | 处理方式 |
|---|---|---|
| 整列数值完全相同 | 相对引用写成了绝对引用 | 检查公式里的$ |
| 累计列不递增 | SUM 起点没锁 | 改成SUM($B$7:B7) |
公式返回#VALUE! | 参数被存成了文本 | 选中参数格,看左上角有没有绿色三角,用“转换为数字” |
公式返回#NAME? | 命名区域拼错或已删除 | 公式 → 名称管理器里核对 |
| 反查等级总是返回 1 | 累计经验列不是升序 | 检查是否有手填值打乱了顺序 |
| 修改参数后表没变 | 计算模式被设成了手动 | 公式 → 计算选项 → 自动 |
| 图表少了一截 | 图表数据源写死了范围 | 改用命名区域 |
| 导出的 CSV 数字带引号 | 单元格格式是文本 | 导出前统一设成数值格式 |
XLOOKUP 报#NAME? | 版本不支持 | 换 INDEX+MATCH |
| 某级经验是 0 | 曲线在该点值小于 0.5 | 外层套MAX(1, ...) |
7. 参数调优的实操心得
7.1 我常用的调参流程
调参不是漫无目的地改数字,按顺序走能省很多时间。
第一步,固定锚点。先确定两个数:1 级升 2 级多少经验、满级前一级多少经验。这两个数定下来,B 就定下来了。锚点怎么选?看你想让玩家玩多久。如果目标总时长 20 小时、每小时 4 万经验,总经验 80 万,反推满级单级经验大概 3 万,锚点就定了。
第二步,看三个自检指标。前 10 级占比、后 10 级占比、相邻级比例。这三个数在表头做成固定单元格,改参数时盯着看。前 10 级占比超过 8%,新手期太长;后 10 级占比超过 45%,后期太拖;相邻级比例如果在中段突然掉到 1.01 以下,说明曲线在那里变平了,玩家会感到“这一段特别轻松”,可能是设计漏洞。
第三步,画图看形状。数字看多了会麻木,图一看就知道问题在哪。我判断曲线好坏有个土办法:把累计经验图的横轴压扁,如果曲线后半段几乎垂直,说明后期设计过重;如果接近一条直线,说明曲线太线性,缺乏层次。
第四步,做极端值测试。把 B 调到 1.0 和 3.0,看表会不会崩。B=1.0 时曲线退化成线性,总经验会很小;B=3.0 时后期经验会爆到几百万。这个测试的目的是确认表格的健壮性——参数在合理范围内怎么改,公式都不能报错。如果 B=3.0 时出现#NUM!,说明某处有幂运算溢出了。
第五步,存档。调到一个满意的版本,立刻复制一份 sheet 命名成“基线_v1”,锁住。后面所有实验都在副本上做。这一步听起来简单,但我吃过亏——调了三小时终于满意,手一抖覆盖保存,找不回来了。
7.2 留一版基线
数值调优是个反复折腾的过程,很容易出现“改着改着发现还不如两小时前那版”的情况。所以基线机制必须有。
我的做法是:主 sheet 叫“当前”,另开一个 sheet 叫“基线”,里面只放参数区的三个数和最终的总经验、各阶段占比这几个关键指标,不放公式不放图表。每次调出一个满意版本,就把参数抄到基线 sheet 里,加一行日期备注。这样回滚的时候不需要整个 sheet 复制,改三个数就回去了。
另一件事是记录改动原因。基线 sheet 里除了参数,加一列“改动说明”,写清楚为什么这么改,比如“后期压力不足,B 从 1.3 调到 1.4”。过两周回头看,你会感谢当时的自己——光看数字是想不起来为什么的。
还有个细节:导出给程序的 CSV 一定要和当前 sheet 的参数一致。我的习惯是在导出 sheet 里加一个校验格,用公式对比导出数据和当前表的满级总经验,不一致就显示红色警告。这个格子救过我一次——改了参数忘了重新导出,程序里跑的还是旧数据,排查了半天才发现问题出在流程上而不是代码上。
把一个升级经验表做到这个程度,其实已经不只是“一张表”了,它是一套可复用的数值实验台。后面接属性成长、接怪物强度、接掉落分布,都是往这个台子上挂新的 sheet,参数联动、图表复用、校验规则照搬。真正花时间的从来不是写公式,而是想清楚你要什么手感,以及怎么用数字把它描述出来。