第九回:拆模块建日报呆码农让游戏学会汇报,搭 SEO 做卡片巧媳妇让游戏学会吆喝
Y 把三千六百行的引擎拆成了四个模块,又给项目接上了每日数据邮件和 SEO 全套;L 这头让排行榜只显示前十名,给分享截图贴上金榜徽章,又做了一个长按生成事件卡片的功能——游戏开始学会自己吆喝了。
以下情节纯属虚构,如有雷同纯属巧合。
开场
3 月 14 号,Y 又开始拆东西了。
上次拆的是 Worker,从一个文件拆成八个。这次拆的是游戏引擎本身。`game-engine.js` 已经长到了 3600 行。所有的验证逻辑、DOM 操作、表达式解析、passport 对接,全堆在一个文件里。L 每天在上面改来改去倒是不介意,但 Y 看着头大。不是因为乱——L 改的东西都能跑——是因为这种文件一旦超过某个体积,谁都不敢随便动。
"该拆了。"他打开 Codex,开始规划怎么拆。
三千六百行变四个模块
Y 告诉 Codex 他想要什么边界,Codex 动手搬。拆出了四个东西。
`shared-validators.js`:所有验证逻辑的权威来源。昵称规范化、passport 域名校验、vibe_uid 格式检查——原来散落在引擎、Worker、passport-bridge 三个地方,各写各的,看着差不多但细节不一样。现在统一一份,其他地方引用。Worker 那边的 `helpers.mjs` 做镜像,测试文件 `validation-parity.test.js` 卡住两边一致。
`dom-refs.js`:把所有 `getElementById()` 调用收到一个 `getEl()` 函数里。一百零三个 DOM 查找,全部走同一个入口。找不到节点的时候统一报错,不会再出现某个角落的 null 引用把页面炸了。
`expression-parser.js`:263 行的表达式求值器,原来藏在引擎中间。tokenizer、逆波兰转换、条件匹配——跟游戏逻辑其实没关系,是纯粹的解析工具。单独拎出来之后补了 88 个测试用例。
`db.mjs`:Worker 那边的数据库操作集中化。八十多行散落在 `passport.mjs` 里的内联 SQL 全部搬过来了。
拆完引擎从 3603 行缩到 3309 行。不算猛。但重要的不是行数。是边界。每个文件有了自己的职责,改的时候不用再在三千行里翻来翻去找该动哪里。
游戏学会了自己汇报
同一天,Y 跟 Codex 做了另一件事:给项目接上了每日数据邮件。
`daily-report.mjs`。Y 把报告需要什么数据、什么格式、什么频率告诉 Codex,Codex 写出来,Y 再调。每天中午上海时间十二点,自动往运营邮箱发一封报告。里面有什么呢——
七天滚动漏斗表:从打开游戏到看到结算页面,每一步有多少人、转化率多少。昨天的漏斗快照。跟前天比的环比变化,涨了显示绿色,跌了显示红色。总运行次数、死因排名、平均寿命。结算质量指标:有没有高价值玩家、邮箱绑定率。
邮件是 HTML 格式的,表格、颜色、上下箭头都有。Y 写这种东西的时候非常认真。报告能不能看得懂,决定了运营能不能做判断。
后来 L 又让 Kimi 在这个基础上加了两块内容:用户来源分布——UTM 标签和 referrer 各占多少——以及玩家局数分布——玩一局的有多少、玩三到五局的有多少、肝到二十局以上的有多少。她还顺手修了时区问题,报告日期从 UTC 改成了北京时间,这样看昨天的数据不会差八个小时。
再后来 Y 又追加了留存队列:D1、D3、D7、D30。靠一张 `user_first_seen` 物化表,从 `session_start` 事件回填首次出现时间。不用全表扫描就能按天分群。
这封邮件从此每天准时到。不管两个人忙不忙,游戏自己会汇报。
排行榜变短了,但更结实了
排行榜这几天也在改。
Y 先让 Codex 修了一个原子性问题。提交分数的逻辑原来是先查旧分、再决定要不要更新。两个请求同时进来就会打架。改成了 `INSERT...ON CONFLICT` 一条 SQL,按聚合方式(sum、min、max)各写一条。请求也加了 15 秒超时,断网和超时分开报错。结算页面上多了一个"重试上榜"按钮,提交失败不用重新玩一局。
然后他改了错误信息的分类。`error_category` 分成 auth、validation、server 三种,客户端先按分类匹配,再按具体 code 匹配,最后兜底。不会再弹一个用户看不懂的技术报错了。
还有一个小细节:服务端现在会告诉你这次提交有没有刷新个人记录。`improved: true` 的才在前端高亮显示。之前是不管有没有进步都弹,弹多了反而没感觉。
L 那头做了一个更干脆的决定:排行榜从显示前一百名改成只显示前十名。`LEADERBOARD_DEFAULT_LIMIT` 和 `LEADERBOARD_MAX_LIMIT` 都改成了 10。beta 阶段用户还少,一百名看着太空了。十名刚好。紧凑。有竞争感。
她让截图会说话
3 月 15 号,L 做了两个跟分享有关的东西。
第一个:结算截图上开始显示排行榜信息了。如果你上了天道金榜,截图底部会多一个金色的框,写着"🏆 天道金榜入榜",下面列着你在哪个榜、第几名、分数多少。发到群里的时候,别人一看就知道你不只是活到了几岁——你还排到了全服前十。
第二个更花功夫:长按事件日志,生成分享卡片。
玩游戏的时候,日志区会不断滚出事件。有些事件很好笑,有些很戏剧。L 想让玩家能把单条事件抓出来分享。她跟 Kimi 描述了她想要的效果,Kimi 帮她做了一个长按触发的机制——600 毫秒阈值,按住之后出现半透明遮罩,松手弹出一个事件卡片。
卡片是 Canvas 画的。木纹底色,顶部有事件类型标签——死亡事件是红底写"劫难",传说事件是金底写"传说",紫色是"奇遇",绿色是"机缘",粉色是"奇缘",普通的是"日常"。中间是事件正文,居中排版,自动换行。底部有游戏属性、二维码、vibesims 品牌和时间戳。
字体用了两套:马山正楷体做正文,像毛笔字;Zpix 像素字体做标注。整张卡片保存成 PNG。文件名叫 `修仙模拟器_[时间戳].png`。
L 做这个功能的时候,大概已经在想传播了。一张结算截图能说明你的一生。一张事件卡片能传递一个瞬间。前者是成绩单,后者是段子。两种东西在社交媒体上都能活。
搜索引擎也找得到了
同一天,Y 在做另一件事。
SEO。
他让 Codex 在 Worker 里加了一个 `seo.mjs` 模块。核心是一个叫 `GAME_SEO_REGISTRY` 的注册表——每个游戏一条记录,写清楚标题、描述、关键词、OG 图片、canonical URL。以后加新游戏,往注册表里加一行,sitemap、JSON-LD、meta 标签就自动生成了。
然后是三个新路由:`/robots.txt` 告诉爬虫哪些能抓哪些不能。`/sitemap.xml` 是索引,指向 `/sitemap-pages.xml`(静态页面)和 `/sitemap-devlog.xml`(开发日志)。devlog 的 sitemap 自动从数据库查,只出已发布的文章。
首页和开发日志页面的 SSR 输出也改了。每个页面头部注入正确的 `<title>`、`<meta description>`、`<link canonical>`、Open Graph 标签和 JSON-LD 结构化数据。搜索引擎和社交平台分享的时候都能抓到对的标题和封面。
还有 PWA。`app/manifest.json`,图标 192 和 512 两个尺寸,scope 设在 `/xiuxian/`,display standalone。加了这个之后,手机上"添加到主屏幕"就能当原生 App 用了。
OG 封面图走 `/xiuxian/assets/og-xiuxian.png`,Worker 代理到 Pages 源站。以后每个游戏各出一张 1200×630 的图就行。
Y 做完这些之后更新了 CLAUDE.md,把 SEO 和多游戏架构的规则写进去了。一个游戏一条注册表记录,其他全自动。
两种吆喝
从 3 月 14 号到 15 号,这个项目同时学会了两种吆喝。
Y 教它跟机器说话。sitemap 让搜索引擎知道这里有什么。JSON-LD 让 Google 能读懂这是一个游戏。OG 标签让分享链接有标题有封面。日报让运营每天知道数据长什么样。这些东西用户看不见,但没有它们,外面的世界找不到这个游戏。
L 教它跟人说话。事件卡片让玩家把好笑的瞬间抓出来发朋友圈。金榜徽章让截图自带炫耀属性。十一条随机文案让每张分享图都不一样。排行榜缩到十个人,紧凑到让你想挤进去。
一个面向搜索引擎,一个面向微信群。都是让游戏被看见。方法完全不同。
到这一步,修仙模拟器已经不只是一个能玩的东西了。它有后端、有数据库、有账号、有排行榜、有日报、有 SEO、有分享卡片、有小红书链接、有开发日志系统。它开始变成一个产品了。
而这个产品里面,每一行文案是 L 写的——她告诉 G 老师她想要什么感觉,再告诉 Kimi 怎么实现。每一根管道是 Y 铺的——他告诉 Codex 架构应该长什么样,再一轮一轮地迭代到能上线。两个人从来没有同时在线超过一小时。两个人用的 AI 工具都不一样。但项目一直在动。