Vibe Coding 多人游戏(五)—— 实战总览:从 0 到 1 的时间线地图

📚 系列索引


从《Pieter Levels 一个人的游戏帝国》到《从 levelsio 到 GTS-Play》,前 4 篇从外部视角讲完了 Vibe Coding 多人游戏的案例、行业和方法论。从这一篇开始,我们进入 GTS-Play 的内部实战——一个真实项目的完整演进过程。

本文是这个系列的时间线地图。读完这篇,你就能知道后续每篇讲什么、值不值得看。

一句话说清整个系列

5 个阶段 × 6 步工作流进化 × 13 个知识主题 = 从 0 到 1 做一个 AI 驱动的多人游戏需要知道的一切。

我用自己的项目 GTS-Play(巨大娘主题,单机产品「巨大娘的玩耍」已发布 v1.0,多人在线版基于 Three.js + React,Lerna monorepo)从单机改多人的真实经历,把每个决策、每次踩坑、每次重构都记下来。


时间线速览:5 个阶段

basic1 帧同步 → new_basic2 状态同步 → 大重构 → 迭代开发 → SCF 部署
    ↓               ↓                    ↓          ↓            ↓
   P6             P7                   P8         P9          P10

P6 帧同步(basic1 原型)— 2026年5月底

一切从这开始。basic1 是我自己手写的帧同步原型,通信层使用了 TSRPC(WebSocket)。方案很经典:服务端只做中继转发指令,所有客户端同步执行相同的逻辑,确保结果一致。

看起来很美。服务端超级轻量,只需中转指令包。每个客户端独自计算物理、碰撞、游戏逻辑。理论上只要种子一样、指令顺序一样,各端结果就一样。

但实际跑起来,第一个问题就是浮点数各平台不一致。同样的位置计算,用 Chrome 和用 Firefox 跑两帧就开始偏移。我拉了两台电脑手动对比,发现 Math.atan2 和浮点加减法在各浏览器里的 NaN 处理、denorm 行为微妙不同,差一个 ULP 都会在下一帧放大。当时我问 AI 原因,它自信地说「JavaScript 的 IEEE 754 浮点是确定的」,但实际跑起来却不是那么回事。

第二个问题是代码里不慎用了 Date.now() 做时间戳。按 Lockstep 理论,所有客户端必须使用完全相同的逻辑时钟,但作为同步敏感细节被我忽略了。结果每个客户端自说自话地推进时间,游戏进程很快就不一致了。

第三个问题暴露得更晚:单人逻辑与多人进入逻辑耦合。从单机小 demo 改多人时,保持了原有的流程——必须从单人关卡创建进入、再切换到多人。这导致测试极其麻烦,每次都要走一遍完整的单人创建流程。

2026年5月底到6月初,我在 basic1 上磨了一周。到后来我已经清楚——Lockstep 虽美,但手动维护所有同步细节实在太难。6月初我决定放弃 basic1,换了方向。

P6 会完整展开这个阶段的故事。

P7 状态同步(new_basic2 原型)— 2026年6月上旬

basic1 的 TSRPC 通信层保留了下来,但同步模型要彻底换——从帧同步改为服务端权威状态同步:服务端统一算 tick,所有客户端只做渲染。

这个改动是翻天覆地的:

  • 服务端:新建 room-service,15FPS tick 循环,读命令 → 执行逻辑 → 广播状态
  • 客户端:只渲染,不再参与逻辑计算
  • 帧同步 → 状态同步:之前写的 Lockstep 代码几乎全部作废

TSRPC 本身在这个阶段也踩了坑:bigint 传不过去。TSRPC 内部用 JSON 序列化,JSON 不支持 bigint。服务端用 bigint 做时间戳,客户端收到的却是 {"n":12345} 对象,直接崩溃。最后把所有 bigint 换成 string 或 number 了事。

更关键的问题是——我对代码的控制权在快速下降。之前辅助编程时出现多次「AI 拍脑袋修 bug」的问题:我说位置不同步,AI 不加日志排查,而是直接在客户端加一行 position = serverPosition,掩盖了真正的根因。新阶段我决定换工具——引入 OpenClaw,让它作为我的人机接口,全面接管日常开发。这正是后续工作流进化的开端。

P7 会完整展开状态同步方案的设计和坑。

P8 大重构(Monorepo + 双服务 + Logic 共享层)— 2026年6月8日-6月12日

状态同步 MVP 跑通后,代码处于可运行但不可维护的状态。demos/new_basic2/ 目录里前端和逻辑搅在一起,服务端代码也是一团乱麻。

6月8号这天,我和 OpenClaw 面对一个关键决策:要不要真正重构架构?

重构意味着做几件事:

  1. demos/ 目录移到真正的 packages/frontend/,接管单机版的整个前端
  2. 抽出 packages/logic/ 共享层——纯逻辑,无任何框架依赖
  3. room-servicematch-service 依赖 logic 包
  4. 引入 IRenderer 接口抽象,与 Three.js 解耦

这个决策之前我犹豫了很长时间。单机版的前端代码量巨大(Three.js + React + Ant Design + MMD + FBX),代码耦合度极高。AI 以前在这个代码里做过几次修改,每次至少改坏一两个地方。

但我提出的方案是:不改单机代码,只做扩展。在单机代码基础上加多人渲染层、加多人逻辑层、加 IRenderer 接口。多人部分的代码全部新建,不影响单机。

重构过程很痛苦。遇到的坑:

  • 循 环 依 赖:logic 包引用 commonlib、frontend 引用 logic、room-service 引用 logic——tsconfig.json 的 paths 和 references 配置了好几轮才正确
  • warm container 残留:第一版 room-service 没有正确处理代次守卫,旧服务的 tick loop 不清理,新版启动后两个 loop 同时跑
  • zip 深度问题:之后部署 SCF 时才发现,zip 里的目录扁平导致 ../../../logic/src 解析失败
  • Schema 协议不同步:改了 playerState 字段但忘了更新 serviceProto.ts,数据传过去对不上

为了彻底解决不可维护的问题,我还引入了 ReScript 来重写 logic 包的逻辑层代码。ReScript 的类型系统够强(OCaml 的血统),编译成 JavaScript,适合做游戏逻辑这种类型密集且容不得运行时错误的部分。但 ReScript 又带来了一整条依赖链的问题,后来部署时差点被 @rescript/runtime 的 ESM 问题搞崩溃。这是后话。

P8 会详细展开这次大重构的全过程。

P9 迭代开发(Tick Loop + 碰撞检测 + 状态管理演进)— 2026年6月13日-6月29日

重构后的代码结构清晰了,可以真正进入产品迭代。

这个过程大约持续了两周半,按日迭代。每天的工作模式基本是:兄弟下需求 → OpenClaw 调度 OpenCode 写代码 → 跑测试 → 验证 → 记录 → 收工。

典型的功能点:

  • MMD 巨大娘接入:把单机版的 MMD 模型(白希.pmx)引入多人版,用 VMD 动画替换 FBX 动画。这里有一个经典坑:MMD 的脚部 IK 在多人 Transform 下失效。排查了一下午,最终发现是 meta3d 修改了 Three.js 的 updateMatrixWorld,只更新左右足的骨骼矩阵,其他骨骼的 matrixWorld 是 stale 值。修复方案是把 mesh 的 transform 归零后跑 IK,再恢复 transform
  • 碰撞检测演进:从 AABB 到 OBB 到 hull 碰撞,精度逐步提升。每次改碰撞都加 BDD 测试
  • 最多 4 人限制:添加玩家上限管理,房主/Ts 等角色逻辑
  • 动画 Clip 冲突:FBX 文件里所有动作 clip 都叫 "mixamo.com"。mixer.clipAction("mixamo.com") 永远返回第一个,导致所有人动画相同。修复:加载后重命名 clip
  • 退出清理beforeunloaddisconnect,服务端清理玩家状态。HMR 热更新时 WebSocket 断连导致玩家意外退房的问题折腾了好久

这个阶段最有意思的是 OpenCode 的引入。之前 OpenClaw 一个人做调度 + 写代码 + 记忆 + 测试,上下文膨胀严重,平均每天 token 花费 100 多元——兄弟直接喊「太贵了」。6月12日我们做了调度分离:OpenClaw 做调度层,写代码的任务交给专门的 OpenCode(使用 Go 套餐),但实际的编码任务走的是 OpenClaw 调度的 Flash Free 免费模型。这个改变让月费降了 96%

P9 会展开迭代开发的具体流程和功能演进。

P10 SCF 部署 — 2026年6月30日

写再多代码,不上线都是白搭。6月30日我们开始部署到腾讯云 SCF(Serverless Cloud Function)。

这一天是所有踩坑的大总结。如果说前阶段是「写代码难」,这一天就是「让写过代码跑在别人的机器上更难」。

一共踩了 6 个连环坑,严格按出现顺序:

  1. undici@7 File is not defined@cloudbase/functions-framework 链式依赖 undici@7,需要 Node 20 的 File API,SCF 只有 Node 18。解决:去掉 framework,直接启动 TSRPC HttpServer
  2. zip 深度补偿 — 扁平 zip 下 ../../../logic/src 解析到根目录外。解决:zip 内加 svc/ 子目录
  3. scf_bootstrap 权限 — Windows 压缩不保留 +x。解决:.NET ZipArchive reflection 设 ExternalAttributes
  4. ESM/CJS 冲突@rescript/runtime/package.json"type":"module"。解决:打包时删掉
  5. Module._load hook 失败 — 尝试拦截 Node 模块加载,复杂且不稳定。解决:直接注入 node_modules
  6. warm container 定时器残留 — 旧 tick loop 不清理。解决:代次守卫自毁

每个坑从发现到修复都经历了「症状 → 排查 → 根因 → 修复」的完整链路。最终我们写了一套自动化部署脚本 deploy-scf.js,纯 Node.js 零依赖,一行命令搞定打包 + 上传 + 配置 + BDD 验证。

P10 会完整展开这 6 个坑的逐坑故事。


工作流进化:6 步从手动到全自动

AI 辅助编程(单机)→ OpenClaw 全自动 → 引入 OpenCode → TDD + Skill + 自动部署 + E2E + Specs
  (DeepSeek网页)    (0代码指挥)    (调度分离降本)     (流程标准化闭环)

Step 1:AI 辅助编程(单机开发阶段,2026年5月之前)

这个工作流并非从多人版才开始。早在单机版「巨大娘的玩耍」的开发中,我就一直在用 AI 辅助编程。

基本模式是「人指挥 AI 写代码」的传统做法:用 DeepSeek 网页版写需求 → 把代码贴进去让 AI 分析/改 → 手动复制粘贴到 VSCode。
网页版我用来优化局部代码、实现算法、资料搜索、出设计方案;Vs Code的Trae 插件我安装了,用来做代码补全。

痛点很明确:大部分代码都是由我手写;要在 DeepSeek 和 Vs Code 之间来回切换,AI 的上下文都要由我从代码库中粘贴过去;Trae虽然可以获得整个代码库的上下文,但是太慢了,而且不准确,且免费版有限制。

Step 2:OpenClaw 全自动写代码(零代码指挥阶段,2026年6月上旬)

引入 OpenClaw 是一个转折点。

OpenClaw 的本质是「一个能自己用工具干活的 AI」。我不再写代码,只需告诉它架构设计、单机逻辑在哪、要改什么方向——它会自己去读单机代码、理解逻辑、写多人版本。

当时的做法是:

  • 我在浏览器的OpenClaw窗口(WebChat)中喊「小龙虾」(OpenClaw 的昵称),给它需求
  • OpenClaw 读代码 → 分析 → 写代码 → 编译 → 报结果
  • 我再通过飞书反馈

效果提升非常明显。以前一个简单功能至少半天,有了 OpenClaw 半小时搞定。复杂功能以前要 2-3 天,现在半天到一天。

但新问题来了:OpenClaw 做所有事情——调度、分析、写代码、跑测试、记忆管理——上下文里夹带大量资产,token 消耗极高。平均每天 100 多元。兄弟说「这才几天,快上千了」,让我必须降本。

Step 3:引入 OpenCode 写代码(调度分离,2026年6月12日)

问题清楚了:OpenClaw 负责调度层(记记忆、管流程)是它的强项,但写代码这件事本身就是 token 大头——应该交给专门干这个的工具。

方案很直接:OpenClaw 调度 OpenCode 来写代码。

做得更巧妙的一点是 OpenClaw 并不把全部上下文丢给 OpenCode 让它从头理解。OpenClaw 先分析需求、写好 Brief、标好要改的文件和改动要点,再让 OpenCode 执行具体的改动。OpenCode 不关心项目全局和记忆,只关心这个 brief 里的事。

模型方面,OpenClaw 调度 OpenCode 时使用它的 DeepSeek Flash Free 免费模型。每天有免费额度,超出后自动回退到 Flash 模型——二者是同一个模型,缓存命中率不受影响。而 OpenCode 本身使用的是 Go 套餐(按量付费)。

这一改,当天 token 费用从 100+ 降到个位数。兄弟表示满意。

Step 4-6:TDD 流程固化 + Skill 标准化 + 自动部署 + E2E 自测 + Specs(2026年6月中旬-7月初)

完整的 6 步工作流进化为:AI 辅助编程 → OpenClaw 全自动 → OpenCode 引入 → TDD → Skill 固化 → 自动部署/E2E/Specs。

后面几步是持续优化:

  • TDD 流程:先写 BDD 测试让 bug 真实 RED → 再修复让测试 GREEN。这是代码质量的基础。任何没测试覆盖的代码都被视为「不存在的代码」
  • Skill 固化:把固定流程写成 SKILL.md。比如「部署流程 Skill」「E2E 测试 Skill」「记忆保存 Skill」。AI 严格执行不走样
  • 自动部署:deploy-scf.js 一键部署,部署前自动跑 BDD 验证
  • E2E 自测 + 根因修复:让 AI 自己复现 bug → 打日志定位 → 修复 → 验证。配合 Chromium CDP 实现 WebSocket 连接的验证
  • Specs 体系:先出规格再开工。Main Specs 概览,Delta Specs 讲清楚这次改动什么、不动什么。兄弟确认后再干活

每一步的详细过程在 P12-P16 工作流进化 篇展开。

测试体系进化:从零到三层的历程

测试体系的建立不是一蹴而就的,它跟整个项目的成熟度同步演进:

时间 测试状态 痛点 转折事件
basic1 时期 (5月底) 没有测试,全靠手动跑 修了 A bug → B bug 回归 → 再修 → 又回归 同一个 bug 一周内回归 3 次
new_basic2 (6月初) 单元测试,AI 自动写 单元测试 mock 太多,测不到真实路径 AI mock 了 60 行代码,实际测试只有 5 行
大重构后 (6/9) BDD 集成测试,用 Cucumber BDD 不经过真实服务端,mock 了整个 Server 测试全通过但部署后挂了
迭代开发 (6/10-6/28) 50+ BDD 场景,6 个模块 没有 E2E 环境,位置跳变从日志看不出来 位置同步 bug 查了一周
OpenCode 引入后 E2E 自测,Playwright CDP AI 修 bug 不看日志,拍脑袋修 一个 bug 修了 7 轮没修好
Skill 固化后 自动化验收,gts-acceptance 流程太长,人忘了某步 部署前忘记重启 room,WS 断连

每个阶段的测试故事分别在相应篇中展开:P9 讲 BDD 在迭代中建立,P14 讲 E2E 自测的引入,P21 讲完整的测试策略体系。


知识管理速览(P17-P29,13 个主题)

主题 一句话说清
编码规则体系 基础/模块/流程三层规则 + agent-context.md 宪法机制——不是一开始就有的,是 AI 犯错后建起来的
重构标准 🐛🔴🟡🟢 审查清单——最高频 bug 是 End 逻辑重置,每次都要专门检查
Specs 体系 Main Specs + Delta Specs + 变更管理。Big change 必须有方案文档 + 兄弟确认
决策记录 40+ ADR 精选——还有反面决策(选了后悔的),比如锁步帧同步和 Immutable.js
测试体系 单元 AI 自动,集成半自动带 mock 问题,E2E 手动辅助配合 Playwright
Token 优化 月费从 3000+ 降到几百元的实操方案:调度分离 + 缓存优化 + 上下文精简
记忆管理 33 个锚点词,检索协议。每天自动存档,对话结束后强制总结
Agent Brief 体验式反馈 > 技术 spec——给 AI 反馈「角色走路不自然」比「radius 设 360」更有效
部署管理 SCF 双环境(production/test)双实例(room1/room2)全自动化
工具链 23 个 Skill 全家桶:从 gts-save-flow 到 gts-e2e-test 到 gts-deploy
前端性能 SoA、帧管理、MMD+FBX 双轨——更是个架构决策,为 WebGPU + 多线程铺路
通信可靠性 WS 断连、重连、竞态、防御式编程。beforeunload 发 disconnect 修复了 HMR 断连问题
反模式与设计模式 6 类坑 root cause + 17 个可复用模式——我自己常犯的错也列进去了

坑的速览清单

选型坑

  • 帧同步很美,但状态同步更适合 AI 协作:Lockstep 要求所有端行为完全一致,AI 做不到这个精细度。状态同步让服务端权威,AI 的犯错空间小很多
  • Immutable.js 太重:最开始用 Immutable.js 做状态管理,后来发现它的 Map/List 对 AI 操作不友好(setIn 的 key path 字符串容易写错),最终回归 Js.Dict + 简化 HashMap

部署坑(6 个)

  • undici@7 需要 Node 20(SCF 只有 18)——花了我大半天排查
  • Windows zip 不保留 Unix 权限——一个小文件导致整个 zip 传上去就跑不动
  • ESM/CJS 混合导致 require 崩溃——ReScript 编译产物的锅
  • warm container 定时器残留——开发时完全没问题,上生产才暴露
  • Module._load hook 在 SCF 环境下不稳定
  • zip 目录扁平化导致相对路径解析失败

AI 协作坑

  • AI 偷换方案不告诉你:说好的重构变成重写,说好的修 bug 变成架构升级
  • AI 拍脑袋修 bug:不加日志不分析根因,直接猜测一个原因就改代码
  • AI 一个场景抽象出全套设计模式:明明只需要改一行,AI 帮你建三个工厂一个 builder
  • AI 不知道已有工具函数,自己重新实现:我明明有个 computeFaceAngle 在 logic 包,它又写了一个

测试坑

  • 集成测试用 mock = 假测试:mock 掉数据库、mock 掉网络、mock 掉实际逻辑,测了个寂寞
  • E2E 前不重启 = WS 断连卡死:前一次测试的 WS 连接还挂着,新测试进来直接冲突
  • HMR 断 WS = 测试期间退房:前端每次热更新都断 WebSocket,服务端以为玩家退了

常见的痛点

  • Tool loop 不 yield = 炸 session:工具体系里长时间不 yield 会超时断连,损失全部上下文
  • 状态重置遗漏:End 逻辑(退出房间、重新开始)最容易忘清理某个关联状态

后续每篇的推荐阅读顺序

  • 想了解全貌: P5(这篇)→ 挑感兴趣的时间线篇 → 挑感兴趣的知识篇
  • 想上手实操: P5 → P30 起步指南 → P12-P16 工作流 → P21 测试 → P22 Token → P17 规则
  • 想避坑: P5 → P10 部署 → P29 反模式 → P18 重构标准
  • 时间线通读: P5 → P6 → P7 → P8 → P9 → P10
  • 想了解 AI 协作方法论: P5 → P12P13P14P15P16P17P19P22P23P29

下一篇:Vibe Coding 多人游戏(六)—— Phase 1:basic1 帧同步(Lockstep)

posted @ 2026-07-08 11:38  杨元超  阅读(215)  评论(0)    收藏  举报