这一页解决一个问题:卷二里出现的每一段代码、每一份配置、每一个数据集,在
pebble/里叫什么、放在哪、怎么跑。 它是一张可检索的清单,也是一份复现手册。
一、这份索引解决什么
卷二的每一节都至少带一个 S / P / W 层的可运行产物。产物多了就需要一张总表,回答三件事:
- 这个产物叫什么名字、放在哪个目录——按章列出
pebble/下的文件,命名规则统一,一眼能定位。 - 它属于哪一层、怎么验证——六层口径(S / P / W / D / X / B)各自的复现命令不同,这一页把它们并排写出来。
- 它依赖什么环境——两个
python3、Node 26、Playwright 复用,任何一处不对都会让「代码看起来错了」,其实只是环境错了。
贯穿全书的仍是《合成小岛》(merge-island)与工具链 pebble。卷二沿用卷一的两条主张:一份 config,三种消费、一份逻辑,两种宿主;config/ 是唯一真源,Python 仿真、TS 内核、浏览器原型读同一份数字。
二、目录约定:为什么卷二用 e<章号>_ 前缀
这是卷二最容易踩的坑,先讲清楚。
卷一的工具链文件用 ch00–ch15 前缀(如 config/ch06_loop.json、sim/ch06_loop.py、core/test/ch06_merge.test.ts)。卷二一律改用 e<章号>_ 前缀(两位章号,如 e01、e04、e16):
config/e04_atlas.json # 卷二的配置
sim/e04_atlas.py # 卷二的仿真
core/src/e04/… # 卷二的内核源码目录
core/test/e04_atlas.test.ts # 卷二的测试
web/build_e04.mjs # 卷二的单文件 HTML 构建脚本
web/verify_e04.mjs # 卷二的 Playwright 无头验收
为什么必须换前缀:node --test 接收的是 glob,不是目录。如果卷二继续用 ch<章号>_,那么无论跑 node --test 'core/test/ch*.test.ts'(卷一)还是跑卷二的用例,通配符都会同时匹配到两卷的测试文件,一条命令里混进另一卷的断言,输出就不可信了。换成 e<章号>_ 之后:
- 卷一:
node --test 'core/test/ch*.test.ts' - 卷二:
node --test 'core/test/e*.test.ts'
两个 glob 互不重叠。注意还要带上两位章号与下划线(e04_ 而不是裸 e*):裸 e* 会误匹配 core/test/economy.test.ts 这种既有文件,所以复现命令里要写全 e04_*.test.ts 这样的精确前缀。
三、按章索引(16 章)
下表把卷二 16 章各自的产物按目录列出。<NN> 是两位章号(01–16),<name> 是该章的文件名(英文 kebab),同一个 <name> 在 config/、sim/、core/test/ 之间保持一致,便于对照。
| 章 | 主题 | config | sim | core/src | core/test | web |
|---|---|---|---|---|---|---|
| 1 | 引擎与框架选型 | config/e01_*.json | sim/e01_*.py | core/src/e01/ | core/test/e01_*.test.ts | web/build_e01.mjs · web/verify_e01.mjs |
| 2 | 工程骨架与构建管线 | config/e02_*.json | sim/e02_*.py | core/src/e02/ | core/test/e02_*.test.ts | web/build_e02.mjs · web/verify_e02.mjs |
| 3 | 渲染与适配 | config/e03_*.json | sim/e03_*.py | core/src/e03/ | core/test/e03_*.test.ts | web/build_e03.mjs · web/verify_e03.mjs |
| 4 | 资源与素材管线 | config/e04_*.json | sim/e04_*.py | core/src/e04/ | core/test/e04_*.test.ts | web/build_e04.mjs · web/verify_e04.mjs |
| 5 | 游戏循环与状态机 | config/e05_*.json | sim/e05_*.py | core/src/e05/ | core/test/e05_*.test.ts | web/build_e05.mjs · web/verify_e05.mjs |
| 6 | 输入与交互实现 | config/e06_*.json | sim/e06_*.py | core/src/e06/ | core/test/e06_*.test.ts | web/build_e06.mjs · web/verify_e06.mjs |
| 7 | 物理与碰撞 | config/e07_*.json | sim/e07_*.py | core/src/e07/ | core/test/e07_*.test.ts | web/build_e07.mjs · web/verify_e07.mjs |
| 8 | 数据、存档与反作弊 | config/e08_*.json | sim/e08_*.py | core/src/e08/ | core/test/e08_*.test.ts | web/build_e08.mjs · web/verify_e08.mjs |
| 9 | 对象池与内存管理 | config/e09_*.json | sim/e09_*.py | core/src/e09/ | core/test/e09_*.test.ts | web/build_e09.mjs · web/verify_e09.mjs |
| 10 | 平台能力适配层 | config/e10_*.json | sim/e10_*.py | core/src/e10/ | core/test/e10_*.test.ts | web/build_e10.mjs · web/verify_e10.mjs |
| 11 | UI 系统与界面实现 | config/e11_*.json | sim/e11_*.py | core/src/e11/ | core/test/e11_*.test.ts | web/build_e11.mjs · web/verify_e11.mjs |
| 12 | 动效与手感打磨 | config/e12_*.json | sim/e12_*.py | core/src/e12/ | core/test/e12_*.test.ts | web/build_e12.mjs · web/verify_e12.mjs |
| 13 | 新手引导实现 | config/e13_*.json | sim/e13_*.py | core/src/e13/ | core/test/e13_*.test.ts | web/build_e13.mjs · web/verify_e13.mjs |
| 14 | 性能优化 | config/e14_*.json | sim/e14_*.py | core/src/e14/ | core/test/e14_*.test.ts | web/build_e14.mjs · web/verify_e14.mjs |
| 15 | 测试与质量保障 | config/e15_*.json | sim/e15_*.py | core/src/e15/ | core/test/e15_*.test.ts | web/build_e15.mjs · web/verify_e15.mjs |
| 16 | 工程化与协作 | config/e16_*.json | sim/e16_*.py | core/src/e16/ | core/test/e16_*.test.ts | web/build_e16.mjs · web/verify_e16.mjs |
读法:想复现第 4 章某节的 S 层仿真,就去 sim/e04_*.py 里找对应的脚本;想跑它的 P 层测试,就 node --test 'core/test/e04_*.test.ts';想看它的 W 层原型,就 node web/build_e04.mjs 产出 HTML 再 node web/verify_e04.mjs 验收。同一章的 config/ / sim/ / core/ / web/ 四类文件用同一个 <name> 串联,这是「一份 config 三种消费」在文件系统上的体现。
四、分层产物索引
按可运行性层级看,卷二的产物分布如下。同一章可能同时出现多个层级:
| 层 | 典型文件 | 产物 | 是否可命令行复现 |
|---|---|---|---|
| S 仿真 | sim/e<NN>_*.py | out/e<NN>_*.json / .csv / .svg | 是 |
| P 内核 | core/src/e<NN>/… + core/test/e<NN>_*.test.ts | node --test 通过 / 失败 | 是 |
| W 网页 | web/build_e<NN>.mjs + web/verify_e<NN>.mjs | web/dist/e<NN>.html + out/shots/*.png | 是 |
| D 静态 | config/e<NN>_*.json(schema)、兼容矩阵、检查清单 | schema / lint 校验结果 | 是 |
| X 不可复现 | 正文标注段 | 真实平台后台、真机帧率、审核结果 | 否(需真实业务环境) |
| B 基准 | sim/*_bench.py | out/*.json(软告警) | 是 |
数据集在这里指两类:① config/ 下的配置表(e<NN>_*.json,全整数、禁浮点,是设计的唯一真源);② out/ 下仿真产出的 *.csv(逐行数据,可导入 pandas 复核)与 *.json(结构化结果)。W 层的 out/shots/*.png 是无头验收的截图证据。
五、环境与复现速查
| 项 | 值 | 说明 |
|---|---|---|
| Python(S 层) | 3.14.6,/opt/homebrew/bin/python3 | 带 numpy 2.5.1 / pandas 3.0.3 / PIL 12.3.0 |
| Python(系统) | 3.9.6,/usr/bin/python3 | 无 numpy/pandas,正文命令一律写全路径 |
| Node | 26.5.0 | 原生跑 TypeScript,strip-only,禁 enum / namespace / 参数属性 / 装饰器 |
| Playwright | 1.59.1,Chromium 已缓存 | 装在 /opt/homebrew/lib/node_modules,用 NODE_PATH + createRequire 复用 |
| matplotlib / scipy | 未安装 | 图表走纯 Python SVG(xml.etree) |
| tsc / tsx / vitest / npm 包 | 均不使用 | 无 package.json、无 npm install |
两个高频陷阱,先记住:
node --test <目录>不发现.ts——必须传 glob:node --test 'core/test/e04_*.test.ts',写目录会报找不到模块。NODE_PATH对 ESMimport无效——W 层脚本里必须createRequire(import.meta.url)之后require("playwright"),不能写import { chromium } from "playwright"。
六、六层逐层复现命令
每一层都有固定的复现命令。<NN> 换成章号(如 e04),<name> 换成该章的文件名:
| 层 | 命令(在 pebble/ 下执行) |
|---|---|
| S | for f in sim/e<NN>_*.py; do /opt/homebrew/bin/python3 "$f" --config config/ --out out/; done |
| P | node --test 'core/test/e<NN>_*.test.ts' |
| W | node web/build_e<NN>.mjs && NODE_PATH=/opt/homebrew/lib/node_modules node web/verify_e<NN>.mjs web/dist/e<NN>.html |
| D | /opt/homebrew/bin/python3 sim/ch00_validate_config.py --config config/ --schema config/ch00_schema.json |
| X | 无命令——真实平台后台 / 真机帧率 / 审核结果需真实业务环境,正文每处已标注 |
| B | for f in sim/*_bench.py; do /opt/homebrew/bin/python3 "$f" --config config/ --out out/; done |
为什么要写 for f in …; do …; done 而不是 python3 sim/e04_*.py:shell 会把通配符展开成多个文件名传给同一个 python3,只有第一个脚本真的执行,其余被当成参数吃掉(卷一第 9 章踩过这个坑)。逐个跑或用 for 循环才安全。node --test 自己展开 glob,不受影响。
七、一键复现与排错
复现任意一节的标准动作,是把该节的 file: 块还原到 pebble/<file>(工作区已就绪),然后跑它 verify: 里那条命令。下面这段 S 层脚本先把环境确认一遍,任何一层不对它都会报出来:
# file: sim/ch00_env_check.py
# tier: S
# verify: /opt/homebrew/bin/python3 sim/ch00_env_check.py
"""环境自检:确认《小游戏开发实战》三卷需要的运行时都在,并打印 pebble 的能力阶梯。
本机有两个 python3,不要混:
/opt/homebrew/bin/python3 = 3.14.6(带 numpy / pandas / PIL),S 层仿真一律用它;
/usr/bin/python3 = 3.9.6(系统自带,无 numpy/pandas),只能跑最朴素的脚本。
"""
from __future__ import annotations
import os
import shutil
import subprocess
import sys
EXPECT_PY = "/opt/homebrew/bin/python3"
def main() -> int:
ok = True
# /opt/homebrew/bin/python3 是符号链接,比较时解析到真实二进制再判断
real = os.path.realpath(sys.executable)
print(f"python {sys.version.split()[0]} ({real})")
if real != os.path.realpath(EXPECT_PY):
print(f" 注意:正文脚本一律用 {EXPECT_PY},当前解释器不同")
try:
import numpy
import pandas
print(f"numpy {numpy.__version__}")
print(f"pandas {pandas.__version__}")
except ImportError as e: # noqa: BLE001
ok = False
print(f"FAIL 缺依赖:{e}(S 层需要 numpy 与 pandas)")
node = shutil.which("node")
if not node:
ok = False
print("FAIL node 未安装(P 层需要 Node 26 或更高)")
else:
ver = subprocess.run([node, "-v"], capture_output=True, text=True).stdout.strip()
major = int(ver.lstrip("v").split(".")[0])
note = "" if major >= 26 else " (低于 26,原生 TypeScript 不可用)"
print(f"node {ver}{note}")
ok = ok and major >= 26
print("pebble 能力阶梯:S 仿真 / P 内核 / W 网页 / D 静态 / X 不可复现 / B 基准")
print("OK 环境就绪" if ok else "FAIL 环境不满足")
return 0 if ok else 1
if __name__ == "__main__":
raise SystemExit(main())
本机实跑输出:
python 3.14.6 (/opt/homebrew/Cellar/python@3.14/3.14.6/Frameworks/Python.framework/Versions/3.14/bin/python3.14)
numpy 2.5.1
pandas 3.0.3
node v26.5.0
pebble 能力阶梯:S 仿真 / P 内核 / W 网页 / D 静态 / X 不可复现 / B 基准
OK 环境就绪
数字对不上时的排错顺序:
- 解释器对吗? 用了系统 3.9.6 会在
import pandas处报ModuleNotFoundError,看起来像「代码错了」,其实是解释器错了。命令一律写/opt/homebrew/bin/python3。 node版本对吗? 低于 26 就没有原生 TypeScript,.ts直接跑不起来。- 前缀对吗? 卷二是
e<NN>_,不是ch<NN>_。写错前缀会匹配不到文件(空跑)或误匹配另一卷。 - 配置被改过吗? 这正是「一份 config 三种消费」该有的行为——改
config/e04_*.json里的参数,sim/、core/、web/三处结果都会跟着变。 - 是不是「同机相对量级」? 正文标了「同机相对量级,绝对数值不可移植」的地方,绝对值依赖机器,只有相对关系可移植。
- 还不对,那就是书错了。 请告诉我具体的节号与数字。
环境口径与全部实跑命令另见卷一的附录 A 。回到卷二目录页 。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。