游戏设计:原型与开发 11:用概率与电子表格搞定骰子系统
- 一、到底什么是游戏平衡?🎮
- [二、从两颗骰子开始:动手复现 2d6 概率分布🎲](#二、从两颗骰子开始:动手复现 2d6 概率分布🎲)
- [三、游戏设计师必知的 7 条概率法则📐](#三、游戏设计师必知的 7 条概率法则📐)
-
- [规则 1:分数、小数、百分数,三者等价](#规则 1:分数、小数、百分数,三者等价)
- [规则 2:概率的取值范围永远在 01(0%100%)](#规则 2:概率的取值范围永远在 01(0%100%))
- [规则 3:概率 = 期望结果数 ÷ 全部可能结果数](#规则 3:概率 = 期望结果数 ÷ 全部可能结果数)
- [规则 4:结果不多就枚举,结果巨大就写程序](#规则 4:结果不多就枚举,结果巨大就写程序)
- [规则 5:互斥事件,"或" 用加法](#规则 5:互斥事件,“或” 用加法)
- [规则 6:独立事件,"和" 用乘法](#规则 6:独立事件,“和” 用乘法)
- [规则 7:逆向思维:发生概率 = 1 − 1- 1−不发生的概率](#规则 7:逆向思维:发生概率 = 1 − 1- 1−不发生的概率)
- 额外重要结论:多颗骰子求和≠线性分布
- 四、写在最后:数学不是代替直觉,而是辅助直觉💡
做游戏设计,很多人把 "平衡" 寄托于灵感与直觉。但真正成熟的游戏设计,背后往往是一套严谨的数学逻辑。尤其是桌面游戏、RPG 类游戏里随处可见的骰子随机系统,看似靠运气,实则每一份随机性都可以被计算、被调试。今天我们就聊聊游戏里的概率数学,以及如何借助表格工具,把模糊的体感平衡,变成可量化的数据平衡。
一、到底什么是游戏平衡?🎮
"平衡" 是游戏开发高频词,但它的含义会随着游戏类型发生变化:
-
多人对抗游戏:平衡≈公平。对称游戏中,所有玩家起始条件、能力完全一致,实现公平相对简单;但不对称游戏里,角色、阵营能力各不相同,一点点数值偏差就会放大体验差距,这也是为什么大量的测试迭代必不可少。
-
单人体验游戏:平衡≈难度曲线。难度陡增会直接劝退玩家,难度平缓又会让玩家觉得缺乏挑战,这正好对应了我们常说的「心流」设计目标。
想要做好平衡,我们离不开概率、权重、正负反馈这些概念。很多设计师习惯依靠感觉调数值,但专业设计师会拿起电子表格工具,把随机机制拆解成看得见的数据。
💡小提示:电子表格不是必选项,草稿纸 + 计算器也可以完成推演,但表格工具的价值在于:快速批量处理数据、生成可视化图表,还能把你的数值逻辑直观展示给策划、程序、美术等非设计岗位的同事。很多游戏设计课程很少教授表格工具,可它却是实战中性价比极高的武器。
本文示例使用 OpenOffice Calc,Excel、LibreOffice Calc、谷歌表格逻辑大体相通,细节操作略有差异。
二、从两颗骰子开始:动手复现 2d6 概率分布🎲
单个六面骰子1d6非常简单,1‑6 每个数字出现机会均等。但当我们掷两颗六面骰子2d6,事情就变得有意思了。
两颗骰子合计会产生 36 种独立结果,总和的取值范围为 2~12,但每个总和出现的机会并不相同。
我们可以在表格软件中复现这个过程,核心用到两个函数:
-
MOD(数值;除数):取余函数,用来模拟第一颗骰子,对数字做取模运算得到 1‑6 循环; -
FLOOR(数值;1):向下取整函数,模拟第二颗骰子; -
COUNTIF(统计区域;目标值):统计某个数字在区域内出现多少次,用来计算各个总和的发生频次。
操作核心要点:
-
相对引用与绝对引用 :公式直接复制,单元格引用会跟着位置自动偏移,这叫相对引用;如果想固定一片统计区域,需要加上
$符号做绝对锁定,例如B$4:AK$4,固定行号避免统计区域发生偏移。 -
退出公式编辑模式一定要按
Esc或者回车,否则点击单元格会意外写入到公式中,引发 bug。
完成计算之后,我们就可以把统计结果生成柱状图,直观看到 2d6 的分布规律。
渲染错误: Mermaid 渲染失败: No diagram type detected matching given configuration for text: barChart title 2d6骰子总和概率分布 x-axis 骰子总和 y-axis 出现频次(总36种组合) bar 2 : 1 bar 3 : 2 bar 4 : 3 bar 5 : 4 bar 6 : 5 bar 7 : 6 bar 8 : 5 bar 9 : 4 bar 10 : 3 bar 11 : 2 bar 12 : 1
图表说明:这是 2 颗 6 面骰子相加的频次柱状图。总和为 7 的时候拥有最高的出现概率(6/36),而 2 和 12 都仅有一种组合可以达成,概率最低。整体呈现中间高、两边低的钟型分布。
同样是骰子,1d6是均匀分布,每一个结果机会均等;而2d6就变成了中间聚集的加权分布。骰子数量越多,结果越向平均值靠拢。
#mermaid-svg-Nf0mA6GVwohvlbYk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Nf0mA6GVwohvlbYk .error-icon{fill:#552222;}#mermaid-svg-Nf0mA6GVwohvlbYk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Nf0mA6GVwohvlbYk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Nf0mA6GVwohvlbYk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Nf0mA6GVwohvlbYk .marker.cross{stroke:#333333;}#mermaid-svg-Nf0mA6GVwohvlbYk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Nf0mA6GVwohvlbYk p{margin:0;}#mermaid-svg-Nf0mA6GVwohvlbYk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk .cluster-label text{fill:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk .cluster-label span{color:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk .cluster-label span p{background-color:transparent;}#mermaid-svg-Nf0mA6GVwohvlbYk .label text,#mermaid-svg-Nf0mA6GVwohvlbYk span{fill:#333;color:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk .node rect,#mermaid-svg-Nf0mA6GVwohvlbYk .node circle,#mermaid-svg-Nf0mA6GVwohvlbYk .node ellipse,#mermaid-svg-Nf0mA6GVwohvlbYk .node polygon,#mermaid-svg-Nf0mA6GVwohvlbYk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Nf0mA6GVwohvlbYk .rough-node .label text,#mermaid-svg-Nf0mA6GVwohvlbYk .node .label text,#mermaid-svg-Nf0mA6GVwohvlbYk .image-shape .label,#mermaid-svg-Nf0mA6GVwohvlbYk .icon-shape .label{text-anchor:middle;}#mermaid-svg-Nf0mA6GVwohvlbYk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Nf0mA6GVwohvlbYk .rough-node .label,#mermaid-svg-Nf0mA6GVwohvlbYk .node .label,#mermaid-svg-Nf0mA6GVwohvlbYk .image-shape .label,#mermaid-svg-Nf0mA6GVwohvlbYk .icon-shape .label{text-align:center;}#mermaid-svg-Nf0mA6GVwohvlbYk .node.clickable{cursor:pointer;}#mermaid-svg-Nf0mA6GVwohvlbYk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Nf0mA6GVwohvlbYk .arrowheadPath{fill:#333333;}#mermaid-svg-Nf0mA6GVwohvlbYk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Nf0mA6GVwohvlbYk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Nf0mA6GVwohvlbYk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Nf0mA6GVwohvlbYk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Nf0mA6GVwohvlbYk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Nf0mA6GVwohvlbYk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Nf0mA6GVwohvlbYk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Nf0mA6GVwohvlbYk .cluster text{fill:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk .cluster span{color:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Nf0mA6GVwohvlbYk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Nf0mA6GVwohvlbYk rect.text{fill:none;stroke-width:0;}#mermaid-svg-Nf0mA6GVwohvlbYk .icon-shape,#mermaid-svg-Nf0mA6GVwohvlbYk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Nf0mA6GVwohvlbYk .icon-shape p,#mermaid-svg-Nf0mA6GVwohvlbYk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Nf0mA6GVwohvlbYk .icon-shape .label rect,#mermaid-svg-Nf0mA6GVwohvlbYk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Nf0mA6GVwohvlbYk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Nf0mA6GVwohvlbYk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Nf0mA6GVwohvlbYk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1d6
均匀分布
2d6
轻度钟型分布
3d6、4d6...
骰子越多,越向均值聚集
10d6
极强的钟型聚集,极端结果几乎很难出现
图表说明:骰子数量增长带来分布形态变化。随着骰子数量增加,极端大小总和出现的可能性急剧降低,大部分结果集中在整体平均数附近。
三、游戏设计师必知的 7 条概率法则📐
手动枚举适合结果数量少的场景,如果是8d6,总组合数达到 6 8 = 1679616 6^8=1679616 68=1679616,手写表格完全不现实,这时就需要掌握基础概率规则。改编自《游戏设计艺术》中经典的概率十条,整理 7 条高频实用规则:
规则 1:分数、小数、百分数,三者等价
概率可以用三种形式互相转换。
例:1d20 掷出 1,概率 1 / 20 = 0.05 = 5 1/20=0.05=5% 1/20=0.05=5
-
分数→小数:直接做除法
-
百分数→小数:除以 100
-
小数→百分数:乘以 100
规则 2:概率的取值范围永远在 01(0%100%)
一件事发生的概率不可能小于 0,也不可能大于 1。不存在 120% 的发生概率哦。
规则 3:概率 = 期望结果数 ÷ 全部可能结果数
1 颗 6 面骰子掷出 6:期望 1 种,全部 6 种,概率 = 1 / 6 1/6 1/6。
一副 52 张牌抽中黑桃:期望 13 种,全部 52 种,概率 = 13 / 52 = 25 13/52=25% 13/52=25。
规则 4:结果不多就枚举,结果巨大就写程序
可能性数量小的时候,把全部情况罗列出来是最简单稳妥的办法,就像我们前面 2d6 表格案例。如果是大量骰子,手动枚举不现实,可以编写代码批量生成全部组合。
规则 5:互斥事件,"或" 用加法
互斥代表两件事不能同时发生。
例子:1d6 掷出 1 或者 2 或者 3, 1 / 6 + 1 / 6 + 1 / 6 = 3 / 6 = 50 1/6+1/6+1/6 = 3/6 = 50% 1/6+1/6+1/6=3/6=50。
规则 6:独立事件,"和" 用乘法
两件事互不影响,需要同时发生,概率相乘。
例子:两颗骰子同时掷出 1, 1 / 6 × 1 / 6 = 1 / 36 1/6 ×1/6=1/36 1/6×1/6=1/36。
规则 7:逆向思维:发生概率 = 1 − 1- 1−不发生的概率
有些场景直接计算 "至少一个 XX" 很麻烦,反过来算 "全都不发生" 会事半功倍。
举个例子:4 颗 6 面骰子,至少掷出 1 个 6 的概率。
直接算要罗列一大堆情况,逆向思考:全部骰子都不是 6 的概率 = ( 5 / 6 ) 4 (5/6)^4 (5/6)4
P 至少 1 个 6 = 1 − l e f t ( f r a c 56 r i g h t ) 4 = f r a c 1296 − 625 1296 = f r a c 6711296 ≈ 52 P_{至少1个6}=1-left(frac{5}{6}right)^4=frac{1296-625}{1296}=frac{671}{1296}≈52% P至少1个6=1−left(frac56right)4=frac1296−6251296=frac6711296≈52
✨实战启发:想要游戏里某个效果 "有不小机会触发,但又不保证一定触发",这条公式是神器。
额外重要结论:多颗骰子求和≠线性分布
单颗骰子每个结果机会均等;但多颗骰子相加,会生成钟型的加权分布。骰子越多,结果越聚集于平均数。
-
如果你希望结果波动很大,极端高低值经常出现:少用多颗骰子相加,优先用单颗大骰子,例如
1d20。 -
如果你希望结果大部分靠近中间值,大起大落要少见:使用多颗骰子相加,例如
3d6。
| 方案 | 分布特点 | 适用设计场景 |
|---|---|---|
| 1d20(单颗大骰子) | 均匀分布,高低结果机会均等 | 暴击、命运判定,追求戏剧性大起大落 |
| 3d6(多骰子求和) | 钟型分布,结果集中在均值 | 角色属性、伤害数值,想要结果稳定少极端 |
表格说明:两种骰子方案的设计取舍。同样是产出 1‑20 区间数值,
1d20和3d6+2的体验完全不同,很多桌游的属性设计就利用了这个差异。
四、写在最后:数学不是代替直觉,而是辅助直觉💡
很多设计师会担心:难道以后调数值全部要听表格数据的?并不是。
电子表格、概率计算不是用来完全取代设计师的感受。它的定位是:
-
帮你看见隐藏在随机背后的真实发生概率,避免 "感觉好像概率还行,实际测试体验完全跑偏";
-
给团队其他岗位提供数据依据,沟通设计改动的时候有据可依;
-
快速迭代原型数值,不用反复做海量人工测试。
游戏平衡永远是迭代的艺术。表格算出的是理论值,最终还要回归玩家测试。数学帮你把起点设置在合理区间,而玩家反馈帮你完成最后的打磨。

如果你正在做桌游原型、RPG 战斗系统,不妨试着把你的伤害、判定逻辑丢进表格跑一遍,说不定会发现很多之前靠感觉完全注意不到的隐藏问题。