08 · 数值公式库与建模
08 · 数值公式库与建模
用法:先定体验指标(常数表),再选公式,再调参,最后蒙特卡洛验证。 不要反过来。
1. 递减收益族(防堆叠)
| 公式 | 形式 | 特点 | 调参直觉 | 适用 |
|---|---|---|---|---|
| 双曲型 | y = A·x/(x+k) |
无硬上限,渐进趋近 A | k = 拐点:x=k 时收益半衰;k 越小越早塌陷 | 暴击率、吸血、减伤、产量 |
| 指数趋近 | y = A·(1−e^(−x/k)) |
平滑,天然硬上限 A | A 定终值,k 定前期陡峭度 | 冷却缩减、闪避、命中 |
| 对数型 | y = k·ln(1+x/k) |
无上限但增长极慢 | 允许硬核无限投入但不破坏平衡 | 后期主属性、声望 |
选择依据:必须有天花板 → 指数趋近;希望保留微小增长 → 双曲/对数。
2. 战斗核心族
| 公式 | 形式 | 用途 |
|---|---|---|
| 减伤 | DR = A/(A+K) |
K = 半效点(A=K 时减伤 50%) |
| EHP | EHP = HP/(1−DR) = HP·(1+A/K) |
EHP 对护甲是线性的,所以护甲不边际递减;递减的是减伤百分比 |
| 期望 DPS | eDPS = dmg × 攻速 × (1 + 暴击率 × (暴击倍率−1)) |
暴击/攻速/暴伤三者乘算 → 三次曲线,这是 ARPG 数值爆炸的数学根因 |
| TTK / TTD | TTK = 敌EHP/我eDPS,TTD = 我EHP/敌eDPS |
对称战斗平衡判据:TTK ≈ TTD |
| 战力指数 | Power ≈ DPS × EHP |
把多维属性压成可比标量;兰彻斯特平方律:有效战力 ∝ 属性²×数量² |
TTK 体验窗口(PVE):
| 对象 | 目标 TTK |
|---|---|
| 小怪 | 1–3 秒 |
| 精英 | 5–10 秒 |
| Boss | 60–180 秒 |
超出窗口 = "打不动"或"秒了",都是体验事故。
组队修正:4 人完美配合 ≈ 16 倍而非 4 倍,组队/Boss 平衡需引入协作系数 α 折减(Power_team = ΣPower × α,α ≈ 0.6–0.8)。
3. 随机性族
| 公式 | 形式 | 用途 |
|---|---|---|
| 期望值 | EV = Σpᵢvᵢ |
平衡的基础 |
| 方差 | Var = Σpᵢ(vᵢ−EV)² |
方差才是挫败来源,不是 EV |
| 变异系数 | CV = σ/EV |
低风险场景 < 0.2;赌博场景可 > 1 |
| 伪随机 PRD | P(N) = C·N,N = 连续未触发次数 |
25% → C≈8.5%;50% → C≈30%。压缩连续不中/连续中都发生,长期频率不变 |
| 硬保底 | P(至少一次) = 1−(1−p)ⁿ |
抽卡 |
| 软保底 | p(n) = p₀ + β·max(0, n−n₀) |
优于硬保底:运气曲线平滑,EV 提升控制在 +10~20% |
PRD 适用场景:暴击、闪避、掉落。凡是"连续 5 次不触发"会引发愤怒的地方,都该用 PRD。
4. 成长族
| 公式 | 形式 | 适用 |
|---|---|---|
| 线性 | a + b·n |
短流程 |
| 多项式 | a·n^k(k=1.5~2) |
通用 |
| 指数 | a·(1+r)^n,r ≤ 0.15 |
放置类 |
| 经验需求 | XP(n) = c·n^1.5~2 |
与战力曲线同阶 |
| S 曲线 | y = L/(1+e^(−k(n−n₀))) |
熟练度、留存养成;把起飞点 n₀ 放在玩家已投入 3–5 小时处最利于留存 |
5. 难度族
| 公式 | 形式 | 说明 |
|---|---|---|
| 心流目标 | D(t) ≈ S(t) × 1.05–1.15 |
上偏焦虑,下偏无聊 |
| 隐形 DDA | ΔD = λ·(胜率 − 0.5) |
λ 小步,且只向下补偿(帮弱者),不惩罚强者 |
| 财富缩放 | 威胁 = f(玩家实力指标) |
Rimworld AI Storyteller:按殖民地财富缩放 + 紧张/喘息节律 |
| Elo/TrueSkill | Elo' = Elo + K·(实际 − 期望) |
PVP;新手 K=32,老手 K=16 |
6. 经济族
| 公式 | 形式 | 用途 |
|---|---|---|
| 净值 | ΔM = ΣSources − ΣSinks |
经济健康 |
| 通胀指数 | B = ΔM/M流通量,|B|>5% 预警 |
监控 |
| 回本时间 | Payback = 成本/边际产出增量 |
跨层级应恒定 |
| 定价锚点 | EV(抽数) = 目标价值 → 反推 p 与保底阈值 |
抽卡/商店 |
7. 建模工作流(五步)
步骤 1:参数外置(最高优先级)
所有数值集中在单一数据源(Excel / Google Sheet / JSON),游戏启动时读取。 改数值不重新编译 —— 这一条能把迭代速度提升一个量级。
禁止:代码里写死 damage = 100 * level
要求:damage = Config.GetValue("player.damage.base") * curve.Eval(level)
步骤 2:解析求解
先算闭式解(EV、EHP、TTK),建立直觉。 产出:常数表 + 关键公式的闭式结果表。
步骤 3:蒙特卡洛
跑 10⁴–10⁶ 次,看分布尾部而非均值。
必须输出三条线:P5 / P50 / P95
关注:P5 是否"灾难性倒霉"(玩家会愤怒),P95 是否"无聊地碾压"
只看均值是头号错误 —— EV 平衡了但体验两极。
步骤 4:机器人跑通全程
写脚本让 AI 按最优/次优策略自动玩 N 局,输出:
- 资源曲线(是否有沙漠/崩溃点)
- 通过率曲线(是否有难度悬崖)
- build 出场率矩阵(哪个 build 从不出场 → 废卡)
步骤 5:小步迭代
每次只改 1 个变量,保留版本快照。多变量同时改 = 丢失因果。 单次调整幅度 ≤ 10%。
8. 工具链选择(独立游戏版)
| 工具 | 适用 | 成本 |
|---|---|---|
| Excel / Google Sheet | 90% 的独立项目够用 | 0 |
| Python + numpy(1000 行内) | 蒙特卡洛、分布可视化 | 低 |
| Machinations | 复杂经济系统的可视化 + 一键蒙特卡洛 | 中 |
| 引擎内 debug 面板 | 实时改参、可视化调试层 | 低,必要 |
| 自建模拟器 | 卡牌/自走棋类必须 | 中高 |
投入控制:工具投入不超过总工时 5%。多数独立项目一张表 + 一个模拟脚本够用。
9. 调参直觉速查
| 你想... | 调什么 |
|---|---|
| 让前期更爽 | 降低递减收益的 k(更早接近上限)或提高线性项 b |
| 让后期不失控 | 降低指数 r,或换递减收益 |
| 让堆叠无效 | 加递减收益 |
| 让某个属性有天花板 | 指数趋近(硬上限 A) |
| 减少玩家愤怒 | 用 PRD 替代真随机;降低高赌注时刻的方差 |
| 让战斗更长/更短 | 调 EHP 或 eDPS,不要同时调 |
| 让某 tier 不再像沙漠 | 缩短该段或插入过渡奖励 |
| 让 build 更分散 | 加正交维度,而非 nerf 头部 |
10. 反模式
| 症状 | 修法 |
|---|---|
| 数值硬编码在代码里 | 参数外置,启动读取 |
| 只算 EV 不算方差 | 输出 P5/P50/P95 三条线 |
| 多变量同时改 | 一次一个,留快照 |
| 靠手感调平衡 | 先建模型,手感只用于微调 |
| 工具投入过大 | 控制在 5% 工时内 |
| 指数成长 r > 0.15 | 降 r 或换曲线 |
| 三个乘算属性同时堆叠 | 用递减收益约束至少一个 |
| 忽视尾部灾难 | 蒙特卡洛必看 P5 |
11. 曲线族详解与调参案例
前一节给的是"用什么公式",本节给的是"为什么是这个公式"以及"参数怎么定"。
11.1 递减收益(双曲型):从上限反推 k
y = A · x / (x + k)
性质:
x → ∞ 时 y → A (A 是硬上限,永远不会到达)
x = k 时 y = A / 2 (k 是半效点)
导数 y' = A·k / (x+k)² (收益随 x 增大而下降 —— 这才是"递减"的来源)
调参两步法(顺序不能反):
- 先定上限 A —— 你想要这个属性的理论最大值是多少?
- 再定半效点 k —— 你希望玩家堆到多少点时拿到一半收益?
完整案例:暴击率
需求:暴击率上限 60%;玩家在 100 点暴击属性时应该达到 30%
求解:0.6 · 100/(100+k) = 0.3
→ 100/(100+k) = 0.5
→ 100 + k = 200
→ k = 100
结果:crit(x) = 0.6 · x / (x + 100)
失败案例:k 设得太小(如 k=10)。 玩家堆到 30 点就已经拿到 45% 暴击,再堆 30 点只多 6% → "堆暴击没意义"。 症状:玩家反馈"后期加属性没感觉"。修法:增大 k。
失败案例 2:k 设得太大(如 k=1000)。 玩家堆到 500 点才拿到 20% 暴击 → "暴击根本堆不起来"。 修法:减小 k,或提高 A。
11.2 指数趋近型:更平滑的无拐点版本
y = A · (1 − e^(−x/k))
性质:
x = k 时 y ≈ 0.632A (注意不是 0.5,和双曲型不同)
无拐点,曲线处处平滑
天然带硬上限 A
选择依据:
| 你想要 | 用哪个 |
|---|---|
| 明确的"半效点"(便于和玩家沟通) | 双曲型 |
| 更平滑的手感、不想要明显拐点 | 指数趋近型 |
| 前期就要有明显收益 | 指数趋近型(前段更陡) |
| 保留"永远还有一点点提升" | 双曲型或对数型 |
11.3 减伤与 EHP 的反直觉(最常被搞错的一条)
DR = A / (A + K) → DR 百分比递减
EHP = HP / (1 − DR) = HP · (1 + A/K) → EHP 对 A 是线性的!
这意味着:护甲在 EHP 意义上并不递减。 很多设计师以为"护甲会递减",于是给护甲加了额外的递减 —— 结果是护甲变成废属性。
推导:
EHP(A) = HP · (1 + A/K) = HP + (HP/K)·A
↑ 这是关于 A 的一次函数(直线)
验证(见 tools/ttk-calculator.py 的减伤曲线输出):
| 护甲 | DR | EHP 倍率 | 每点护甲的 EHP 增量 |
|---|---|---|---|
| 0 | 0% | 1.00× | — |
| 25 | 20% | 1.25× | 恒定 |
| 50 | 33.3% | 1.50× | 恒定 |
| 100 | 50% | 2.00× | 恒定 |
| 200 | 66.7% | 3.00× | 恒定 |
失败案例:用 DR = A/(A+K) 却以为护甲递减,于是没做任何约束。
玩家很快发现"无脑堆护甲最优" → 支配策略(见 principles/03 §3)。
修法二选一:
- 改用
DR = 1 − e^(−A/K)—— 这时 EHP 才真正递减 - 保留线性 EHP,但用其他机制约束(护甲占用装备槽、有重量、有上限)
11.4 期望 DPS 的三次曲线(数值爆炸的数学根因)
eDPS = 基础伤害 × 攻速 × (1 + 暴击率 × (暴击倍率 − 1))
└── 因子1 ──┘ └因子2┘ └──────── 因子3 ────────┘
三个因子相乘 → 同时堆叠时是立方级增长。
tools/ttk-calculator.py 的实测演示:
| 阶段 | DPS | 相对基础 |
|---|---|---|
| 基础 | 30.0 | 1.00× |
| +攻速 50% | 45.0 | 1.50× |
| +暴击率 50% | 67.5 | 2.25× |
| +暴伤 到 3.0 | 90.0 | 3.00× |
| +基础伤害 100% | 180.0 | 6.00× |
修法:对至少一个乘数使用递减收益。 常见做法:暴击率用递减收益,攻速设硬上限,暴伤作为稀有属性。
11.5 成长曲线选择矩阵
| 曲线 | 形式 | 前期手感 | 后期风险 | 适用 |
|---|---|---|---|---|
| 线性 | a + b·n |
平稳 | 平淡 | 短流程、叙事 |
| 多项式 | a·n^k (k=1.5–2) |
爽 | k>2 时失控 | 通用 RPG |
| 指数 | a·(1+r)^n |
极爽 | r>0.15 时 30 级后失控 | 放置类 |
| 对数 | k·ln(1+n/k) |
无感 | 无 | 硬核后期投入 |
| 递减收益 | A·n/(n+k) |
快 | 无 | 防堆叠属性 |
选择顺序:先问"我希望玩家在第 10 小时感觉变强了多少?" → 再选曲线 → 再定参数。
经验需求对齐:XP(n) = c·n^1.5~2 必须与战力曲线 n^k 同阶。
不同阶 → 出现"某几级特别慢"(资源沙漠)。
12. 工具链对接
本节的公式已全部实现为可运行脚本,见 tools/。
| 你要验证什么 | 跑哪个脚本 | 输出 |
|---|---|---|
| TTK 是否落在体验窗口 | tools/ttk-calculator.py |
TTK + 窗口判定 |
| 减伤曲线是否符合预期 | tools/ttk-calculator.py |
DR / EHP 倍率表 |
| 暴击堆叠会不会爆炸 | tools/ttk-calculator.py |
乘算堆叠对比 |
| PRD 的尾部压缩效果 | tools/ttk-calculator.py |
真随机 vs PRD 对比 |
| 分布尾部(P5/P95) | tools/monte-carlo.py |
五档分位 + CV |
| 经济通胀与回本时间 | tools/economy-sim.py |
回本表 + 存量曲线 |
| 支配策略与废卡 | tools/balance-scanner.py |
三张表红项 |
使用纪律(三条,缺一则工具白做):
- 参数外置 —— 数值必须从外部数据源读取,否则脚本无法接入
- 固定种子 —— 版本对比必须可复现
- 每次只改一个变量 —— 多变量同时改会丢失因果
投入控制:工具总投入不超过总工时 5%。 多数独立项目:一张表 + 一个模拟脚本就够了,不要自研通用框架。
PRD 的 C 值必须反查(tools/ttk-calculator.py 内置 PRD_C_TABLE):
目标 25% → C≈0.0847,不是 0.302(那是 50% 的常数)。
用错 C 值会让实际触发频率翻倍,而长期均值看起来"正常"。
13. 参考
- GDC《Math for Game Programmers》系列(Balancing / Juicing)
- machinations.io —— 可视化经济建模
- Liquipedia PRD 表 —— 完整 C 值常数表
- Adams & Dormans《Game Mechanics: Advanced Game Design》—— Machinations 方法论
tools/README.md—— 本节公式的可运行实现