背景
v0.6 完成了 Runtime 架构升级(MemoryRuntime + Deterministic Assistant),但玩家的游戏体验仍然停留在”输入文字 → 等待回复”的阶段。缺少两个关键的东西:
- 目标感:玩家不知道为什么做某件事、做完了会怎样
- 角色感:许知微作为叙事中的”同伴”,在代码层面存在,但在游戏里几乎不可见
v0.5 的 standalone MVP 证明了”可以玩”,v0.6 证明了”架构正确”。v0.7 和 v0.8 要证明”像一个游戏”。
v0.7:游戏骨架
Goal Engine — 让判断脱离代码
v0.7 的核心架构决策是:把”目标是否完成”的判断逻辑从 engine.js 的 if-else 中抽出来。
在此之前,engine.js 中的目标判定是硬编码的:
if (known_clues.includes('clue_1') && known_clues.includes('clue_2')) { ... }
这种方式有三个问题:
- 新增目标必须改 engine 代码(设计者不能独立迭代内容)
- 判定逻辑不可测试(与游戏状态紧耦合)
- 复杂的 AND/OR/NOT 组合无法表达
GoalEngine 引入了声明式 DSL:
type GoalCondition =
| { all: GoalCondition[] }
| { any: GoalCondition[] }
| { not: GoalCondition }
| { clueKnown: string }
| { factConfirmed: string }
| { eventOccurred: { type: string; npcId?: string } }
| { stateEquals: { key: string; value: unknown } }
设计意图:条件可以递归嵌套,纯函数可测试,JSON 可外置。这意味着未来新增目标不需要碰 engine 一行代码,只需要写一个 JSON 文件。试玩版目前使用 8 个目标定义,全部在 materials/runtime/goals/ 中管理。
目标还有两个重要行为决策:
- 建议性而非强制性:多目标并行,玩家自选次序
- 未完成顺延:本轮未完成的目标,下一轮依然存在(不设永久失败)
这套设计受 testplay.md 的设计约束直接影响:v0.8 的 8 个目标需要 loopRange(目标仅在特定轮次激活)、completionCondition(DSL 复合条件)、derivedFrom(派生线索自动生成)三项扩展能力。
指令系统 — 让操作脱离输入框
v0.5 时代,查看线索、人物、记忆等功能完全是”隐藏命令”——玩家不知道可以输入什么,只能靠猜或者阅读文档。
v0.7 做了两件事:
- 建立 CommandRegistry:12 条指令,4 个分类(information / memory / action / interaction),统一在
command-registry.json中声明 - 建立 CommandMatcher:基于触发词的多级匹配(精确 → 包含),支持中文别名
指令栏的设计意图不是”把功能暴露出来”,而是三轮渐进学习曲线:
| 轮次 | 指令栏 | 许知微 |
|---|---|---|
| 1 | 全部可见,新指令闪烁 | 主动发起对话,介绍目标和指令 |
| 2 | 常显 | 停滞时轻提示 |
| 3 | 末次常显 | ”你很熟练了” |
| 4+ | 收起为图标 | 静默 |
设计判断:第 4 轮收起指令栏,不是因为功能不重要,而是因为玩家的注意力应该逐步从”学习操作”转向”沉浸在故事中”。这是一个游戏设计决策,不是 UI 简化。
许知微 — 让助手回归叙事
许知微在 v0.6 中已经作为 Deterministic Assistant 流水线存在,但她的出现方式极其生硬——“在 init() 末尾直接调用 showXuWelcome()”。
v0.7 做了三个关键调整:
- 入场时机:从代码初始化移到游戏开始后 2 秒自然触发,配合立绘入场动画(
PortraitIntro) - 渐进引导:第 1 轮主动介绍系统,第 2 轮轻提示,第 3 轮确认熟练度
- 对话限制:许知微只能解释指令和复述进度,绝不执行指令或替玩家决策
这里有一个重要的设计边界:许知微的能力模型是”向导”而非”代打”。她可以告诉你”查看线索”怎么用,但不会帮你查线索。这个边界在代码层面由 showXuPanel() 强制(只读查询,无状态写入)。
UX/UI — 场景驱动 + 渐进学习
v0.7.1 做了视觉优化:
- NPC 芯片改为纯名称显示(去掉角色标签前缀)
- 许知微芯片用绿色样式区分(
lt-companion-chip) - 场景卡片在对话中收缩为 80px 半透明提示
- 对话面板撑满场景卡片到输入栏之间的全部空间(修复了对话框被限制在底部 35vh 的问题)
- 立绘入场动画(NPC 头像从全屏缩放到对话框头像位)
v0.8:《寒灯初醒》内容上线
为什么叫”寒灯初醒”
v0.5-v0.7 的游戏内容本质上是”技术 Demo 的占位文本”——角色叫”小宁”、“赵乘警”、“沈墨寒”,场景叫”第七节车厢”,对话是模板文本。
v0.8 的目标是:让这个 Demo 变成一个可以认真玩的试玩版。
“寒灯初醒”这个名字来自试玩结尾。试玩版结束时,爆炸仍会发生(因为当前循环无法完全阻止),但三号车厢已清空。许知微低声说:“寒灯。这不是第一次循环,也不会是最后一次。” 主角的真实代号在此之前从未揭示——这不是一个”名字彩蛋”,而是叙事节奏的收束点。
故事重构
时间线:从 08:45→09:00 改为 14:00→14:15(15 分钟),下午的环境光更适合车厢场景的视觉设计。
角色调整:
| 旧 | 新 | 理由 |
|---|---|---|
| 赵乘警(警察 gatekeeper) | 陆成(列车乘务员) | 警察的身份在 1939 年过于敏感,乘务员更自然 |
| 沈墨寒(灰大衣 misdirection) | 灰衣乘客(不揭示阵营) | 早期版本过度暗示了沈的身份,破坏悬疑 |
| 小宁妈妈(隐藏 NPC) | 移除 | 试玩版 3 轮内无法触发该节点,属于过早优化 |
内容结构
v0.8 建立了完整的内容外置体系:
| 文件类型 | 数量 | 说明 |
|---|---|---|
| 角色 JSON | 5 个 | 小宁、陆成、灰衣乘客、许知微、主角寒灯 |
| 场景 JSON | 3 个 | 二号车厢、三号车厢、连接处 |
| 线索 JSON | 8 条 | 爆炸时间、小宁异常、金属声、灰衣人帆布包等 |
| 目标定义 | 8 个 | 分三轮激活,含 loopRange 和 DSL 复合条件 |
| 世界时间线 | 8 个事件 | 14:02-14:15 编排 |
| 结算文本 | 3 轮 | 每轮含结果、线索、建议、许知微复盘 |
| 开场字幕 | 1 组 | 电影式文字滚动 |
| 试玩结尾 | 1 组 | 收束叙事 |
所有内容文件存放在 materials/runtime/ 下,由 engine 和 server 在启动时加载。代码和内容终于分开了。
新的开场
v0.5 的开场是三段背景文字铺垫。
v0.8 重写为电影式字幕滚动:“铁轨在黑暗中震动。窗外是流动的夜色。你知道 15 分钟后这列火车会爆炸。你不记得自己是谁,但你记得这件事。许知微合上笔记本,抬起头:‘你又醒了。’”
设计意图:信息密度更高,悬念植入更早。玩家在第一个画面就知道”列车会爆炸”和”这不是第一次循环”。
许知微的首次对话也从泛泛的”有什么需要帮助的吗”改为故事整合的引导:“先别急着证明自己是不是疯了。我们已经试过几次了。这一轮先确认三件事:你在哪里,爆炸什么时候发生,身边谁见过异常。”
她不是一个”客服”,她是经历过循环的人。
设计判断
为什么 GoalEngine 不做成”任务列表 UI”
一个常见的产品直觉是:有了目标系统,就应该在 UI 上做一个”任务列表”,像 RPG 游戏那样。
我们刻意没有这样做。理由:
- LoopTrain 是互动叙事,不是 RPG。任务列表会把玩家的注意力从”故事”拉向”清单检查”。
- 目标栏只显示当前一个目标(最近激活的),不显示全部。多目标并行是引擎能力,不是 UI 承诺。
- 正反馈卡片是叙事的节奏点——它告诉玩家”你做对了”,但不告诉玩家”还剩几个”。
指令栏 vs 对话输入:为什么不做成统一输入框
12 条指令和对话共享同一个输入框(通过触发词匹配区分)。
理由:手机屏幕太小。在 390px 宽度下,两个输入框会让可点击区域互相重叠。当前方案用同一个输入框 + 触发词匹配,保留空间给对话日志。
为什么保留”结束对话”按钮而不是自动结算
对话轮数限制到了不会自动结束,玩家需要手动点”结束对话”。
理由:强制结束会让玩家觉得”系统打断了我的对话”。手动结束让玩家保留控制感。
当前状态
v0.7 + v0.8 合计:
- 系统:GoalEngine DSL 判定器、12 条指令系统、许知微主动引导、立绘入场动画
- UI:场景驱动布局、目标栏/指令栏、3 轮渐进学习曲线、正反馈卡片、对话面板扩展
- 内容:《寒灯初醒》完整试玩(5 角色、3 场景、8 线索、8 目标、3 轮结算、开场/结尾)
- 架构:内容完全外置化(JSON 文件管理),代码与内容分离
后续计划
- LLM Bridge 真实接入 — 当前 Mock 模式对话可玩但文本单一;接入 DeepSeek 实现 NPC 动态对话
- Playwright 回归测试 — 覆盖开场→对话→结算→失败→下一轮的完整玩家路径
- 线上
/play/game评估 — 本地 SLT 稳定后,评估切换生产环境的时机