文章

Vibe Coding 游戏开发教程 · 下篇 |《深夜画廊》:一句题材词,做出有声有色的视觉小说

立项输入只有一句话:"换成悬疑访谈吧"。文生图定人、图生图定情绪、BiRefNet 抠图、TTS 一人一音色(基频 291/202/158 Hz 仪器验证)、Lyria 2 氛围乐——五种生成能力各有引擎契约承接。附立绘管线四阶段图与 WASM 实机成品截图。

Vibe Coding 游戏开发教程 · 下篇 |《深夜画廊》:一句题材词,做出有声有色的视觉小说

两款游戏,两篇教程。这是下篇。上篇《小马拼图:一张截图、一段视频,复刻一款解谜手游》的输入是截图和视频;这一篇更极端——立项输入只有一句话:

换成悬疑访谈吧。

设定、人物、剧情、素材规格,全部由 agent 统筹展开。我后续的参与方式仍然只有两种:一句话要求(如”一个人物固定一个音色”)和一张截图指出问题(如”三个人都是有背景的”)。照旧,没有写过复杂提示词

先看成品——两张都是 WASM 版在浏览器里跑着的实机截图:

深夜画廊 · 选人界面 选人界面:三位证人并排立在夜画廊里,透明立绘干净地叠在光影背景上。

深夜画廊 · 审讯中 审讯苏晴:打字机字幕正在逐字打出,她身后墙上挂着的正是《凝视者》——那幅”盯着你”的画。

第 1 步 · 一句话,agent 统筹出一个游戏

“换成悬疑访谈吧”六个字,agent 自己统筹出的东西:

我说的agent 自己分解出的活
“悬疑访谈”剧情设定:名画《凝视者》深夜失窃,审讯三位证人,选问法、观表情、指认真凶
(没说)三个角色的人设:林薇(网球教练)/ 苏晴(会计)/ 陈墨(美术史讲师)——职业、性格、各自的隐情
(没说)素材规格清单:3 人 × 3 表情立绘 + 干净/惊悚两版场景 + 3 条开场语音 + 1 首氛围循环乐
(没说)分支对话树引擎:打字机字幕、选项、按剧情切换表情、指认结局
(没说)踩了 Lua 保留字 goto 的坑(分支跳转字段),自己改名 dest 绕开

一个 Lua 文件装下全部。一句话是意图,清单是 agent 的。

第 2 步 · 立绘管线:同一张脸,四个阶段

视觉小说最难的是角色一致性——同一张脸要有平静/紧张/惊惧三种状态,还得是透明底。这条管线全程 Floniks(经 MCP,agent 直接调):

立绘管线四阶段 林薇的四个阶段,全部真实素材:① 文生图底图(注意带烘焙背景——这就是后来被截图报障的问题);② BiRefNet 去背景得到透明立绘;③④ 以底图为源图生图,只动表情不动脸。棋盘格 = 真透明通道。

  • 底图定人:每位证人一张文生图,定脸、定服装、定气质;
  • 图生图定情绪:紧张/惊惧从底图派生——同一张脸,只动表情;
  • 场景同理:干净画廊底图 → 图生图惊悚变体(墙上多一幅盯着你的画,见上面审讯截图)。

第 3 步 · 语音:一人一音色,修到有仪器证据为止

我的要求一句话:

创建 TTS 的时候需要根据人物性别来生成不同性别的声音,要用不同的音色,一个人物固定一个音色。

第一版”修好了”——听感上却总觉得像一个人。我追问一句”人物的声音性别问题解决了吗”,agent 做了声学分析:三人基频 128 / 122 / 131 Hz,几乎同一个声源——根本没修好

根因挖出来:voice 参数写错了字段,平台处理器实际读的是 config.voiceId(camelCase、数组形式)。修正字段 + 原生 pitch 后再次测量:291 / 202 / 158 Hz,少女/播报/御姐三个真实音色。

这一步的教训值得写进任何 AI 工作流:“修好了”必须有可测量的证据,听感不算。

第 4 步 · 截图报障:一张图,三个根因

上线后我发了一张截图:

声音有了,但是没有抠图。三个人都是有背景的,一个都没抠图。

agent 的处理链:

  1. 9 张立绘全部过 Floniks removeBackground(BiRefNet)抠成透明底(管线图的第 ② 步,当时是补课);
  2. 顺手修掉网页版无声:浏览器把 AudioContext 初始为 suspended,首次点击时 resume;
  3. 抠图后画面还是旧的——浏览器缓存在作祟,贴图整体改名破缓存。

第 5 步 · 一句现象描述,换来一次引擎重构

整个游戏的音轨管理有点混乱,出现多重音轨交叉或同时播放的问题,需要系统性的解决这一问题。

注意我说的是现象和”系统性”三个字,没有指定方案。agent 把整个引擎的音频重构成三条声道:SFX 可叠但帧内去重、Music 同名不重启、Voice 播新台词先掐旧的——对话永不重叠。补丁能修一次,模型能修一类,这次重构惠及全部 12 个游戏。

为什么这么丝滑:两个平台咬合成一条流水线

《深夜画廊》把 rust_bevy_lua_game(引擎平台)× Floniks(素材平台) 的协同用到了最满——五种生成能力,每一种的落点都是引擎早已定好的契约:

能力Floniks 侧(生成即工具,MCP)rust_bevy_lua_game 侧(数据即约定)
立绘文生图底图 + 图生图表情贴图同名同尺寸落位,一行 API 切表情
抠图removeBackground(BiRefNet)透明 PNG 直接叠加,无需引擎改动
语音minimax TTS,voice_id 固定进角色表Voice 声道:先掐后播,对话永不重叠
氛围乐文生音乐(Lyria 2)Music 声道:同名不重启,场景重入不炸音
剧情—(agent 直接写分支树,LLM 即编辑器)Lua 热重载秒级验证;分支跳转纯数据可 diff

和上篇同一个结论,这里更明显:不是 agent 快,是路上没有关卡。 每个生成产物落进约定路径就能被引擎吃掉,agent 在一个会话里跑完”写剧本 → 生成立绘 → 抠图 → 配音 → 配乐 → 测试 → 上线”整条链,零人工搬运。

完全跨平台:原生与 WASM,同一份代码

《深夜画廊》是 12 个游戏里对跨平台要求最苛的——它有立绘、语音、音乐、分支剧情,而这一切在三种形态下同一份 gallery.lua,字节级相同:

  • 原生 macOS / iOS:mlua(C Lua)驱动,iOS 上 120Hz ProMotion;
  • Web / WASM:ottavino(纯 Rust Lua VM)驱动,暴露一模一样的 game API;连”浏览器音频要首次点击解锁”这种平台差异,也被引擎层吸收,脚本毫无感知。

本文开头两张成品图,就是 headless 浏览器打开 WASM 版实机截的——立绘、光影、打字机字幕都在浏览器里原样跑。一条链接点开即玩:https://maweis.com/rust_bevy_lua_game/;同一场审讯,iPhone 上又是原生帧率。

方法论小结

  1. 一句题材词就能立项,人设/剧情/素材规格的统筹是 agent 的活。
  2. 角色一致性 = 底图 + 图生图,表情/场景变体都从底图派生(见管线图)。
  3. 修复要有仪器证据:基频分析证伪了”听起来修好了”。
  4. 截图报障,根因归 agent:一张图挖出抠图、缓存、无声三个根因。
  5. 描述现象,不指定方案:”系统性解决”四个字换来一次正确的引擎重构。
  6. 双平台咬合 + 全跨平台:五种生成能力各有引擎契约承接;一份脚本,原生与浏览器同跑。

开源脚手架:rust_bevy_lua_game(仓库内 docs/blog/ 有开发博客与会话存档);素材平台 Floniks(MCP 端点 api.floniks.com/api/v1/mcp);编排 Claude Code。上篇:《小马拼图:一张截图、一段视频,复刻一款解谜手游》。

本文由作者按照 CC BY 4.0 进行授权