常用函数与曲线
数值设计的基本词汇是「函数形状」:线性、幂、指数、饱和、封顶、阶梯、分段门槛——绝大多数平衡问题都能归约为「选对形状,再定参数」。本章把公式引擎的能力面展开成一份速查 + 曲线库:内置函数逐个给语义,常用曲线逐条给图、给用途、给陷阱。
::: note 图从哪来(程序是唯一事实源) 本章全部曲线图由 crates/sandtable-core/examples/formula_curves.rs 生成:对每条表达式调用真实求值器(formula::compile + Formula::eval)逐点采样,输出 SVG 到 docs/public/formulas/。不手画图——表达式编译不过,生成器直接 panic,文档曲线与求值器能力不可能漂移。改了求值器后重绘:
cargo run -p sandtable-core --example formula_curves:::
内置函数(7 个)
| 函数 | 参数 | 语义 | 典型用法 |
|---|---|---|---|
min(a, b) | 2 | 取小 | min(10, x) 增益封顶 |
max(a, b) | 2 | 取大 | max(1, atk - def) 保底伤害 |
clamp(x, lo, hi) | 3 | 截断到区间 | clamp(x, 0, 100) 硬上限 |
floor(x) | 1 | 向下取整 | floor(x / 10) * 10 档位化 |
ceil(x) | 1 | 向上取整 | 资源按整消耗 |
abs(x) | 1 | 绝对值 | 距离 abs(a - b) |
round(x) | 1 | 四舍五入(.5 远离零) | 展示层整数化 |
边界行为(有界求值,热点路径不 panic):
- 除以 0、对 0 取模 → 0(仿真高频路径不允许崩溃);
- 负数的分数次幂(
x^0.5且 x < 0)→ NaN,NaN 参与比较一律为假——曲线定义域含负值时先套abs或分段; - 未知函数名、参数个数不对,在加载期编译就报错,不进运行期。
运算符
| 类别 | 运算符 | 备注 |
|---|---|---|
| 算术 | + - * / % | % 为浮点取模,同样「模 0 → 0」 |
| 幂 | ^ | 右结合;是幂不是异或(与 C 系语言不同) |
| 比较 | == != < <= > >= | 结果为 1.0 / 0.0,可直接参与算术 |
| 逻辑 | && || ! | 返回 1.0 / 0.0;不做短路(表达式纯函数无副作用,两侧都会求值) |
| 条件 | a ? b : c 三元 | 只求值被选中的分支;可嵌套(右结合),用于分段 |
优先级从低到高:?: → || → && → == != → < <= > >= → + - → * / % → 一元 - ! → ^。即 -x^2 是 -(x^2),复合条件如 a < b && c > d 不用加括号。
能力边界(如实)
没有的内置项:exp / log、三角函数、sigmoid / smoothstep、表查找(lookup 不在表达式层)、随机数。想要时的处理:
| 想要 | 当前写法 | 说明 |
|---|---|---|
| 指数增长 | 1.15^x | 常数底幂近似,适合短区间;x 大后爆炸,见下图 |
| 对数(减速) | k * x^0.5 或 k * x / (x + c) | 幂减速 / 有理饱和,通常就是对数曲线的合格替身 |
| S 形(sigmoid) | 分段三元逼近 | 只在确需平滑拐点时;多数场景阶梯或分段已够 |
| 线性插值 | a + (b - a) * t | 本来就是算术,直接写 |
| 真正的 exp / log / 表查找 | 不硬凑 | 按07 章「后续扩展」加 builtin,或等脚本层;表达式层堆技巧换来的可读性损失不值得 |
曲线库
图内标签维持英文(不依赖 CJK 字形的纪律),读图口径看各节说明。
增长形态:线性 / 幂 / 指数
| 表达式 | 形状 | 典型用途 | 陷阱 |
|---|---|---|---|
x | 线性 | 每级 +N 的基础成长;预算完全可预测 | 长线运营后期激励不足 |
x^1.5 | 缓幂递增 | 「越往后越贵/越强」的温和版 | 与线性前期几乎重合,玩家感知不到差异 |
x^2 | 二次递增 | 战力对属性的放大(如伤害 ∝ 攻击²) | 属性 10 倍 → 输出 100 倍,叠加增益后极易战力爆炸 |
1.15^x | 指数(常数底幂) | 短期增益(≤30 级的 buff/活动) | 约 x≈58 越过 x^2,之后一路碾压,长线成长禁用;常与 floor/取整配合做「翻倍感」 |
选型口径:能线性不幂,能幂不指数——指数只留给有明确终止点的短期系统。
收益递减:三种饱和
| 表达式 | 形状 | 典型用途 | 陷阱 |
|---|---|---|---|
10 * x^0.5 | 开方减速,无界 | 单调递减但不上限的收益(边际递减仍鼓励堆叠) | 前期过陡:0→1 的增益约是 9→10 的 6 倍,新手期体感夸张 |
100 * x / (x + 25) | 有理式,渐近 100 | 软上限:命中率、减伤、转化率 | 参数 c 就是「半饱和点」(x=c 时恰 50),改 c 等于挪拐点,别再叠系数 |
100 * (1 - 0.5^x) | 几何饱和 | 「每级收益减半」的观感;保底概率随次数上升 | 到 99% 后基本躺平,若希望「后期仍有事做」需叠阶梯补偿 |
三者都满足「投入越多边际越低」,差别在是否有上界、拐点在哪。有上界的两条,拐点参数(c 与底数)是平衡的主要旋钮。
封顶:硬上限 vs 软上限
| 表达式 | 形状 | 典型用途 | 陷阱 |
|---|---|---|---|
clamp(x, 0, 100) | 硬上限,超线全蒸发 | 合法值域兜底;命中率/暴击率封顶 | 超线投入零回报,玩家会精确堆到 100 就停——如果希望「继续堆仍有意义」就别用它 |
x < 50 ? x : 50 + (x - 50) * 0.5 | 拐点折减,超线减半计 | 软上限:仍鼓励堆叠,不惩罚过头 | 斜率 0.5 是第二旋钮;多段折减可嵌套三元,但超过两段就该考虑换形状 |
口径:砍收益用 clamp,保体验用折减。图上虚线是未封顶参考线,封顶的意义就是让它离开对角线。
量化阶梯
| 表达式 | 形状 | 典型用途 | 陷阱 |
|---|---|---|---|
floor(x / 10) * 10 | 10 级一档的台阶 | 档位制(段位、品质、副本难度) | 档位边界处投入无即时回报(「9 级白升」),需配进度展示或下一档预告;虚线参考线与台阶的差就是「沉没投入」 |
round(x / 10) * 10 | 就近取档 | 四舍五入版,边界观感更匀 | 上下半档不对称浮动,配结算系统时注意可复现口径 |
分段门槛
| 表达式 | 形状 | 典型用途 | 陷阱 |
|---|---|---|---|
x < 30 ? 0 : (x < 60 ? (x - 30) * 2 : 120) | 门槛-线性-跳档三段:30 前为 0,30-60 线性到 58,60 起跳档至 120 并封顶 | 通行证、赛季任务、解锁制收益 | 门槛前全灰是最伤留存的段,常配保底起始值或首达奖励;斜率 2 是「补偿期追赶速度」旋钮 |
嵌套三元是本引擎表达分段的标准写法:条件从左往右排,值放分支里。段数 ≥ 4 时可读性明显下降,优先考虑是否真需要这么多段。
组合模式
实际公式几乎都是上述形状的复合,两个高频配方:
战力/伤害(前期陡、后期封):min(cap, base * x^1.5)
带门槛的饱和收益:100 * max(0, x - 20) / (max(0, x - 20) + 25)调参口诀:先定形状,再定参数;每个参数只干一件事(系数管整体幅度,幂次/底数管弯曲,clamp/门槛管边界)。调「手感」时一次只动一个旋钮,否则曲线族变化无法归因——这与参数扫描的 OAT 方法是同一条纪律。