Files
blind/林夏.md
T
xslandClaude Opus 4.6 3f53623f12 v2完整版:VoxCPM2语音 + 多轮对话 + 文档重写
- 用VoxCPM2生成128条v2语音(林夏回应+NPC反应+Round3对话)
- story.js全部切换到VoxCPM2音频路径
- engine.js: BGM音量减半(0.2→0.1)、MSG-14前导语支持、结局E用周南新语音
- 林夏.md完整重写:6手势交互、主角有声音、VoxCPM2本地TTS方案
- batch_tts_voxcpm.py: 新增linxia角色、修复self_3y音色、添加全部v2台词
- 新增voice/linxia/参考音色

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-19 14:38:28 +08:00

786 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 林夏
> **类型**:情感 / 叙事 / 轻悬疑 · 纯音频交互游戏
> **目标用户**:20–25 岁年轻女性(大学生 / 初入职场 / 一线城市独居)
> **时长**:约 3045 分钟
> **核心隐喻**:所有人都给你发语音,没有人发文字。今晚你必须一条一条听完,才能决定明天的自己长什么样。
> **核心原则**:每次选择都必须产生玩家能听到的、不同的后果。
---
## 0. 一句话概述
24 岁的林夏,被分手、被裁员、被妈妈催婚,全发生在同一个周五。她把手机调成静音、躲进合租屋,关上灯。屏幕坏了——只剩耳机能听、手指能摸。今晚 16 个人会发来电话和语音。她接谁、不接谁、怎么回——每一个回应都会被对方听到,每一段对话都会走向不同的结局。
---
## 1. 玩家与角色
### 1.1 玩家是谁
- **林夏**,24 岁,西部小城来的姑娘,北漂第 3 年。
- 国贸某外企新媒体专员,今天上午刚被裁员(合同到期不续签)。
- 与男友"阿哲"恋爱 2 年 4 个月,今天下午刚被分手("我们性格真的不合")。
- 与一位大学闺蜜"小美"合租,但小美今晚去了酒吧。
- 老家妈妈让她"五一回来相亲"。
- 微信未读:99+。
- 屏幕:今天上午地铁挤碎了的那块屏,现在显示一片黑——但触控、震动、声音都还在。
### 1.2 林夏不是"听者"——她会说话
与传统音频叙事游戏不同,**林夏有自己的声音**。玩家的每次选择都会触发林夏的一句语音回应,对方会听到并做出反应。这构成了完整的对话:
```
NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回应做出反应
```
林夏的声音设计:**疲惫但清醒、克制但有温度、年轻但不幼稚。**
---
## 2. 核心玩法
### 2.1 六种手势——零视觉依赖
| 手势 | 全局含义 | 情感色彩 | 触觉反馈 | 音效 |
|------|---------|---------|---------|------|
| **单击** | 接受 / 回应 / 继续 | 中性应答 | 轻触感 | 清脆短音 880Hz |
| **双击** | 跳过 / 忽略 | 不耐烦 / 赶时间 | 短促双震 | 下行滑音 587→392Hz |
| **右滑** | 温暖 / 靠近 | 接纳、关心 | 流畅长震 | 上行音 440→659Hz |
| **左滑** | 冷淡 / 推开 | 疏离、敷衍 | 干脆短震 | 下行音 523→349Hz |
| **上滑** | 好奇 / 追问 | 想了解更多 | 快速上弹震 | 快速上行 523→784Hz |
| **下滑** | 沉默 / 退缩 | 说不出口 | 极轻慢震 | 极轻低音 330Hz |
> **不需要记位置**。每个方向有固定的情感含义,贯穿全游戏。右滑永远是温暖,左滑永远是冷淡。
### 2.2 对话流程(多轮交互)
```
┌─────────────────────────────────────────────────┐
│ [提示音 + 震动] │
│ 消息到达 │
│ → 单击播放 / 双击跳过 │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ NPC 语音播放(Round 1
│ "你被甩了我都听说了,明天陪你喝酒" │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ [三音上行提示音 · 等待选择] │
│ 玩家操作:右滑 / 左滑 / 上滑 / 下滑 / 单击 │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ [打字音效 ~0.8s → 发送音效] │
│ 林夏的语音播放 │
│ 右滑 → "好啊,明天不见不散。" │
│ 左滑 → "再说吧。" │
│ 上滑 → "你怎么知道的啊?" │
│ 下滑 → [一秒沉默] │
│ 单击 → "嗯。" │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ NPC 反应播放(Round 2)—— 因选择而异 │
│ │
│ 右滑: "行!那我叫上安安!老地方见!" │
│ 左滑: "……好吧,你休息。" │
│ 上滑: "还不是阿哲那个大嘴巴告诉我的……" │
│ 下滑: "??你还活着吗哈哈。算了不打扰你了" │
│ 单击: "嗯是什么意思啊哈哈!行吧你休息" │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ [可选] Round 3: 继续对话(仅部分路径触发) │
│ NPC 追问 → 再次选择 → 林夏回应 → 对话结束 │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ → 等待 → 下一条消息到达 │
└─────────────────────────────────────────────────┘
```
### 2.3 关键设计
| 方面 | 说明 |
|------|------|
| **林夏的声音** | 每次选择后都有语音回应(或有感知的沉默) |
| **NPC 反应** | 根据选择播放完全不同的反应语音 |
| **选择的感受** | 五个方向各自导向不同的音频内容 |
| **对话长度** | 温暖/追问路径更长、信息更多;冷淡/沉默路径更短 |
| **独家信息** | 追问路径可获得其他路径听不到的剧情细节 |
| **玩家驱动** | 没有时间限制,消息不会自动推进,完全由玩家操作控制节奏 |
| **双击跳过** | 在林夏回应或 NPC 反应播放中,双击可跳过当前语音,加速流程 |
### 2.4 关键机制:「正在输入…」
当林夏回应前,耳机里会播放微弱的**键盘敲击声** → 发送音效 → 然后才播放林夏的语音。这 ~1 秒的"打字等待"让选择感觉真实——你不是在"选菜单",你是在"发语音"。
---
## 3. 消息全列表(16 条)
| # | 来源 | 形式 | 关键内容 | 有无选择 |
|---|------|------|---------|---------|
| 01 | 妈妈 | 电话 | "今晚煮了你爱吃的排骨" | 接听 → 无选择(自动回应) |
| 02 | 闺蜜小美 | 语音 ×3 | "你被甩了我都听说了,明天陪你喝酒" | 5 方向 + 追问 Round 3 |
| 03 | 外卖小哥 | 电话 | "你的麻辣烫到了" | 接听 → 无选择(自动回应) |
| 04 | 前任阿哲 | 语音 | "我的 AirPods 落你那了" | 5 方向 + 温暖/追问各有 Round 3 |
| 05 | HR 王姐 | 语音 | "离职流程周三前办完就行" | 5 方向 |
| 06 | 室友安安 | 语音 | "我在 Helens!快来!" | 5 方向 |
| 07 | 寝室群 | 语音 ×5 | 室友 A 升职,@林夏 | 5 方向 + 温暖 Round 3 |
| 08 | 未知号码 | 电话 | 呼吸声 6 秒后挂断 | 无选择(悬疑钩子) |
| 09 | 小美(醉)| 语音 ×4 | "阿哲大三追过我" | 5 方向 + 追问 Round 3 |
| 10 | 妈妈 | 文字 | "睡了吗?" | 5 方向 + 温暖/追问各有 Round 3 |
| 11 | 阿哲 | 电话 | "我能过去拿吗?就 5 分钟" | 5 方向(关键分支锚点) |
| 12 | 周南 | 语音 | "初三坐你后桌那个" | 5 方向 |
| 13 | 周南 | 语音 ×4 | "今天我生日" | 5 方向 + 温暖 Round 3 |
| 14 | 妈妈 | 电话 | "你不接电话我有点担心" | 接听 → 自动回应,条件分支 |
| 15 | 自己 | 系统通知 | 3 年前录的备忘录 | 单击播放 / 双击跳过 |
| 16 | 妈妈 | 长语音 ×8 | 妈妈 24 岁那年的故事 | 无选择(结局前奏) |
---
## 4. 16 条消息详细设计
### MSG-01 妈妈 · 电话
**类型**:来电(铃声 + 长震动)
**Round 1**(接听后):妈妈说"今晚煮了你爱吃的排骨,你什么时候回来呀。"
**接听后自动**:林夏轻轻说"嗯……好。"(无选择)
**不接** → 直接跳到下一条。
**状态**:接听 → MOM_LINK = true
---
### MSG-02 小美 · 语音 ×3(教学关)
**Round 1**:小美连发三条语音:
- "林夏!你还活着吗!"
- "你被甩了我都听说了——别难过啊"
- "明天陪你喝酒,去不去"
**选择 + Round 2**
| 方向 | 林夏说 | 小美反应 | profile |
|------|--------|---------|---------|
| 右滑 | "好啊,明天不见不散。" | "行!那我叫上安安!老地方见!别放鸽子啊!" | engagement+2 |
| 左滑 | "再说吧。" | "……好吧。你要是改主意了随时说。" | avoidance+1 |
| 上滑 | "你怎么知道的啊?" | "还不是阿哲自己发朋友圈!你不知道吗?他发了又秒删……" | engagement+1**独家信息** |
| 下滑 | [沉默 2s] | "??你还活着吗哈哈。好了不烦你了,有事找我。" | avoidance+2 |
| 单击 | "嗯。" | "嗯是什么意思啊哈哈!行吧你休息。" | — |
**追问 Round 3**(上滑后,小美说完"他发了又秒删"):
- 右滑 → 林夏:"他……发了什么?" → 小美:"就发了一张你们以前的照片,配了一句'值得'。两分钟就删了" → **独家信息**
- 其他 → 对话结束
---
### MSG-03 外卖小哥 · 电话
**类型**:来电
**Round 1**:外卖小哥:"你好,你的麻辣烫到了,在楼下。"
**接听后自动**:林夏说"好,我下来拿。"(无选择)
**不接** → 漏接。
---
### MSG-04 阿哲 · 语音
**Round 1**:阿哲说"那个……我的 AirPods 好像落你那了,方便帮我寄一下吗?"
| 方向 | 林夏说 | 阿哲反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "你那边……还好吗。" | "我……挺好的。" [停顿] "你呢?" | engagement+2 |
| 左滑 | "知道了。明天寄。" | "嗯……谢了。" | HE_BACK=false, avoidance+1 |
| 上滑 | "你怎么不自己来拿。" | "我……不太方便过去。" [欲言又止] | engagement+2 |
| 下滑 | [沉默 3s] | [等 5 秒] "那……我晚点再说吧。" → 阿哲撤回消息 | HE_BACK=null, avoidance+2 |
| 单击 | "好。" | "谢谢。" | HE_BACK=false, engagement+1 |
**右滑 Round 3**(阿哲问"你呢?"后):
- 右滑 → 林夏:"不太好。" → 阿哲:"……对不起。" [沉默]
- 左滑 → 林夏:"挺好的。" → 阿哲:"那就好。"
- 下滑 → [沉默] → 阿哲:"……那,晚安。"
**上滑 Round 3**(阿哲说"不太方便"后):
- 上滑 → 林夏:"为什么。" → 阿哲:"我……" [长停顿] "没什么。你寄吧。" → HE_BACK=false
- 右滑 → 林夏:"算了,不问了。" → 对话结束
---
### MSG-05 HR 王姐 · 语音
**Round 1**:王姐说"小夏,离职流程周三前办完就行。别有压力。"
| 方向 | 林夏说 | 王姐反应 | profile |
|------|--------|---------|---------|
| 右滑 | "谢谢王姐。" | "别客气。你还年轻,出去看看也好。这行业你知道的。" | engagement+1 |
| 左滑 | "好的,知道了。" | [无反应,对话结束] | — |
| 上滑 | "王姐……有什么建议吗?" | "你想听真话?别在这行耗了。你有本事,去试试别的。" → **独家** | engagement+2 |
| 下滑 | [沉默] | [无反应,对话结束] | avoidance+1 |
| 单击 | "好。" | [无反应,对话结束] | — |
---
### MSG-06 安安 · 语音
**Round 1**:安安在酒吧里大喊:"我在 Helens!快来!今天 happy hour"(背景很吵)
| 方向 | 林夏说 | 安安反应 | profile |
|------|--------|---------|---------|
| 右滑 | "在哪儿啊?我看看吧。" | "对面那个!我给你留了位置!赶紧赶紧!" | engagement+2 |
| 左滑 | "不了,你们玩好。" | "好吧!那我给你寄一杯!" [大笑] | — |
| 上滑 | "你喝了多少了?" | "才两杯!我清醒得很!……好吧三杯。快来!" | engagement+1 |
| 下滑 | [沉默] | "喂?信号不好吗?好吧以后再约!" | avoidance+1 |
| 单击 | "嗯。" | "嗯是来还是不来啊!算了回头聊!" | — |
---
### MSG-07 寝室群 · 语音 ×5
**Round 1**:群消息连续播放:
- 室友 A"我升主管了!!!"
- 室友 A"请你们吃饭!!"
- 室友 B"我的天!恭喜!!"
- 室友 B"必须喝一个!"
- 室友 C"@林夏 你也来吧"
| 方向 | 林夏说 | 群里反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "恭喜恭喜!太厉害了!" | 室友 A:"哈哈谢谢!我请客!你最近怎么样啊?" | GROUP_REPLY, engagement+2 |
| 左滑 | "恭喜。" | [群里继续聊别的,没人注意到你的冷淡] | avoidance+1 |
| 上滑 | "怎么升的啊?讲讲!" | 室友 A:"说来话长!我们老板走了我就顶上了哈哈" → **独家** | GROUP_REPLY, engagement+2 |
| 下滑 | [沉默,不回群] | [群里继续热闹,但渐渐没人 @ 你了] | avoidance+2 |
| 单击 | [发一个表情] | 室友 B:"林夏发表情了哈哈" | GROUP_REPLY |
**右滑 Round 3**(室友 A 问"你最近怎么样"后):
- 右滑 → 林夏:"挺好的~你请客我去!" → 室友们欢呼
- 左滑 → 林夏:"还行。" → 室友 B:"你是不是有事啊?没事可以跟我们说"
- 下滑 → [沉默] → 对话自然结束
---
### MSG-08 未知号码 · 电话(悬疑)
**类型**:来电
**接听后**:只有呼吸声,6 秒后自动挂断。然后心跳音效。
**无选择**——悬疑钩子。
**不接** → 未接来电。
---
### MSG-09 小美(醉)· 语音 ×4
**BGM 切换为悬疑风格**
**Round 1**(四段醉酒语音):
- "林夏……我跟你说个事"
- "阿哲那个人……大三的时候追过我"
- "但我没答应他……因为我知道你喜欢他"
- "我到现在都没跟你说……对不起"
| 方向 | 林夏说 | 小美反应 | profile |
|------|--------|---------|---------|
| 右滑 | "小美……你没事吧。" | "我没事……我就是喝多了想告诉你……你别怪我。" [哭] | engagement+2 |
| 左滑 | "我知道了。" | [沉默 3 秒] → 小美撤回了刚才的语音 | avoidance+1 |
| 上滑 | "你……为什么现在才说。" | "因为你今天……我怕你以后恨我……" | engagement+2 |
| 下滑 | [沉默] | [10 秒后] 小美撤回了刚才的语音 | avoidance+2, nostalgia+1 |
| 单击 | "嗯。" | "你不生气吗?" [等 3 秒] "算了……当我没说" | nostalgia+1 |
**上滑 Round 3**(小美说"怕你恨我"后):
- 右滑 → 林夏:"我不恨你。" → 小美 [大哭]:"谢谢你……谢谢你林夏"
- 左滑 → 林夏:"我得想想。" → 小美:"好……对不起" → 对话结束
- 下滑 → [沉默] → 小美:"你还在吗?" → [长沉默] → 小美撤回
---
### MSG-10 妈妈 · 文字消息
**Round 1**:消息到达提示音。TTS 朗读三个字——"睡了吗?"
| 方向 | 林夏说 | 妈妈反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "没有,怎么了?" | 妈妈发语音:"没事,就是想起来问一下。今天怎么样?" | MOM_LINK, engagement+2 |
| 左滑 | "嗯,要睡了。" | 妈妈发文字:"好,早点睡。" | MOM_LINK, avoidance+1 |
| 上滑 | "妈……你能打电话给我吗?" | [电话铃响] 妈妈打来了!接听后:"怎么了夏夏?" | MOM_LINK, engagement+3 |
| 下滑 | [不回复] | [30 秒后] 妈妈发文字:"看到了回一下。" | avoidance+2 |
| 单击 | "嗯。" | 妈妈发文字:"嗯就是睡了?晚安。" | MOM_LINK |
**上滑路径**(妈妈打来电话后,说"怎么了夏夏?你声音不对。"):
- 右滑 → 林夏:"没什么……就是有点累。" → 妈妈:"累了就回家歇几天。妈煮了排骨。"
- 上滑 → 林夏:"妈,我辞职了。" → 妈妈 [沉默 2 秒]:"辞了就辞了。回来再说。"
- 下滑 → [沉默,只有呼吸] → 妈妈:"不想说就不说。妈在这呢。"
**右滑路径**(妈妈问"今天怎么样"后):
- 右滑 → 林夏:"还好。" → 妈妈:"那就好。别太晚了。"
- 上滑 → 林夏:"不太好。" → 妈妈:"怎么了?" → [可继续展开]
---
### MSG-11 阿哲 · 电话(关键分支)
**类型**:来电
**Round 1**(接听后):阿哲说"那个……AirPods 的事,我能过去拿吗?就 5 分钟。"
| 方向 | 林夏说 | 阿哲反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "可以。但只有 5 分钟。" | "好,我马上过来。谢谢。" → [挂断] | HE_BACK=true |
| 左滑 | "不方便。我明天寄给你。" | "好吧……那谢了。" → [挂断] | HE_BACK=false |
| 上滑 | "为什么非要今天来?" | "我……明天要出差。" [停顿] "就 5 分钟。" | 继续选择 |
| 下滑 | [沉默 → 挂断音] | [电话断了] | HE_BACK=false |
| 单击 | "不了。" | "好。" → [挂断] | HE_BACK=false |
**如果 HE_BACK=true** → 消息 12 之前插入门铃声 + 敲门声场景。
---
### MSG-12 周南 · 语音
**Round 1**:周南说"林夏,是我,周南。初三坐你后桌那个。好久没联系了,今天突然想到你。"
| 方向 | 林夏说 | 周南反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "周南!你还记得我啊!" | "当然记得。你是我们班最安静的那个。" [轻笑] | ZHOUNAN_DEPTH+1, engagement+2 |
| 左滑 | "嗯,你好。" | "嗯……好久不见。你现在在哪呢?" | ZHOUNAN_DEPTH+1, nostalgia+1 |
| 上滑 | "你怎么有我微信的?" | "你忘了吗?毕业那天你加的我。不过你可能早忘了哈哈。" | ZHOUNAN_DEPTH+1, nostalgia+2 |
| 下滑 | [沉默] | "你是不是不记得我了?没关系的。就是突然想说声嗨。" | nostalgia+1 |
| 单击 | "嗯……好久不见。" | "是啊,好久不见。" | ZHOUNAN_DEPTH+1 |
---
### MSG-13 周南 · 长语音 ×4
**BGM 切换为周南主题(温暖吉他)**
**Round 1**(四段语音):
- "高考那年我没考好,去复读了"
- "后来上了个二本,学了个不喜欢的专业"
- "毕业回了老家,在我爸店里帮忙"
- "今年刚来北京出差……今天是我生日"
| 方向 | 林夏说 | 周南反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "一个人在北京,还好吗?" | "还行吧。习惯了。你呢?你在北京过得好吗?" | ZHOUNAN_DEPTH+1, engagement+2 |
| 左滑 | "你讲了好多。" | "是吗?不好意思,话太多了。" [轻笑] | ZHOUNAN_DEPTH+1, nostalgia+1 |
| 上滑 | "……生日快乐。" | [沉默 2 秒] "谢谢你。你是今天第一个跟我说的。" | ZHOUNAN_DEPTH+1, ZHOUNAN_SHARE, nostalgia+2 |
| 下滑 | [沉默] | "不好意思突然说这么多……那,晚安。" | avoidance+1 |
| 单击 | "嗯,我都听到了。" | "嗯。谢谢你听我说。" | ZHOUNAN_DEPTH+1 |
**右滑 Round 3**(周南问"你在北京过得好吗"后):
- 右滑 → 林夏:"不太好。今天也是我的坏日子。" → 周南:"那我们算是难兄难妹了。" [轻笑] → **ZHOUNAN_SHARE**
- 左滑 → 林夏:"还行吧。" → 周南:"那就好。"
- 上滑 → 林夏:"你为什么突然联系我?" → 周南:"因为……今天生日嘛,想起以前的事。想起你。"
---
### MSG-14 妈妈 · 第二次电话
**类型**:来电
**Round 1**:妈妈说"夏夏,你不接电话我有点担心。你还好吗?"
接听后 → 林夏自动说"嗯,我没事。"
妈妈:"真的没事?"
- 如果 MOM_LINK=true"好。那早点睡。妈爱你。"
- 如果 MOM_LINK=false"你今天怎么都不理妈妈?出什么事了吗?" → 可选择回应
**状态**:接听 → MOM_LINK=true
---
### MSG-15 时光胶囊 · 系统通知
**特殊消息**:系统提示音 + "3 年前的今晚,你给自己录过一段备忘录。"
- 单击播放 → 根据玩家画像播放不同版本(avoidance / engagement / nostalgia → SELF_RECORD=true
- 双击跳过 → avoidance+2
*无选择交互。这条消息是独白时刻。*
---
### MSG-16 妈妈 · 长语音 ×8(结局前奏)
**条件**MOM_LINK=true 时播放
妈妈讲她 24 岁那年的故事:
1. "夏夏。妈妈知道你今天电话不接。妈妈不催你,就是想跟你说一件事。"
2. "你 24 了对吧。妈妈 24 那年——你应该没听我说过——"
3. "那时候妈妈在广州,给一个香港老板做秘书,1995 年。那年我也被分手过。也被开除过。"
4. "那年我哭得特别难看。后来呢?后来就没什么后来了。我回了老家,认识了你爸,生了你。"
5. "我想跟你说,那年夏天我没死,是因为我外婆每天晚上都给我打一个电话。"
6. "她不说什么,就问我今天吃饭了没。"
7. "我现在打给你,你不接也没事。妈妈给你发个语音,你什么时候想听就听。你不用回我。"
8. "你今天接了好多电话吧。妈妈不打扰你了。晚安。"
*无选择交互。让玩家安静听完。播放结束 → 触发结局判定。*
---
## 5. 分支判定变量
| 变量 | 触发条件 | 影响 |
|------|---------|------|
| **MOM_LINK** | 是否接妈妈两次电话中的至少一次 / 是否回复"睡了吗?" | 决定 MSG-16 是否播放 |
| **HE_BACK** | 是否答应阿哲过来拿耳机(MSG-11 右滑) | 决定 MSG-12 前是否插入门铃场景 |
| **GROUP_REPLY** | 是否在寝室群里回复过(MSG-07 非沉默) | 决定彩蛋消息 |
| **ZHOUNAN_DEPTH** | 给周南的回应深度(累计) | 决定结局字幕 + self-3y 版本 |
| **ZHOUNAN_SHARE** | 是否在周南对话中选择"分享自己"MSG-13 上滑/右滑 R3 右滑) | 决定隐藏结局 |
| **SELF_RECORD** | 是否听完 3 年前备忘录(MSG-15) | 决定结局类型 |
| **PROFILE** | 全场行为聚合画像:avoidance / engagement / nostalgia | 决定 self-3y 版本 + 环境音 |
### PROFILE 计分细则
| 行为 | avoidance | engagement | nostalgia |
|------|-----------|------------|-----------|
| 漏接电话(不动) | +2 | 0 | 0 |
| 双击跳过消息 | +2 | 0 | 0 |
| 单击回"嗯" | +1 | 0 | 0 |
| 左滑冷淡回应 | +1 | 0 | 0 |
| 下滑沉默 | +2 | 0 | +1 |
| 右滑温暖回应 | 0 | +2 | 0 |
| 上滑追问 | 0 | +2 | 0 |
| Round 3 中选择"分享自己" | 0 | +1 | +1 |
最终取分数最高项作为 PROFILE 标签。
---
## 6. 结局系统(5 种)
### 结局 A · "明天就明天吧"
**触发**MOM_LINK=true + SELF_RECORD=true + 至少回了周南或群里 1 条
- 妈妈语音播完。林夏起身,环境声:走到厨房、烧水、坐在小板凳上。
- 字幕:"明天再说。"
- 烧水壶滴的一声 → 游戏结束。
### 结局 B · "AirPods 我寄给你"
**触发**HE_BACK=false + 回过周南至少 1 条
- 林夏给阿哲发了文字"我寄给你,地址发我"。
- 字幕:"寄出。"
- 一个"清醒地走"的结局。
### 结局 C · "全部已读"
**触发**:未读数=0 且回复≤2 条
- TTS:"今晚共 16 条新消息,已读 16,未回复 14。"
- 长沉默。字幕:"今晚就这样。"
### 结局 D · "明天再开机"
**触发**:未读数≥8
- 系统提示电量 1%。触感震动 + 白噪 → 彻底黑。
- 第二天清晨鸟叫。字幕:"早上好。"
### 结局 E · 隐藏 · "周南,我也记得你"
**触发**ZHOUNAN_DEPTH≥2 + ZHOUNAN_SHARE + PROFILE=engagement 或 nostalgia
- 周南秒回:"那我下次出差来北京,请你吃饭好不好。"
- 系统播放林夏的轻笑——这是全游戏唯一一次林夏主动笑出声。
- 字幕:"好。"
---
## 7. 音频设计
### 7.1 音频资产分类
| 类别 | 说明 | 数量 | 制作方案 |
|------|------|------|---------|
| **林夏语音** | 主角对话回应(通用 + 专属) | ~55 条, ~100s | VoxCPM2 声音克隆 |
| **NPC Round 1 语音** | 各角色主要台词 | ~30 条, ~300s | VoxCPM2 声音克隆 |
| **NPC Round 2/3 反应** | 对话分支反应 | ~54 条, ~205s | VoxCPM2 声音克隆 |
| **self-3y 三版** | 时光胶囊备忘录 ×3 | 3 条, ~150s | VoxCPM2linxia 音色) |
| **UI 短音效** | 提示音、选择反馈、打字/发送 | ~12 种 | Web Audio 合成(无需文件) |
| **BGM** | 4 段主题音乐 | 4 段 | Suno / Udio 生成 |
| **环境音** | 卧室夜噪、酒吧、街声 | 4-5 段 | Freesound (CC0/CC-BY) |
| **角色铃声** | 来电铃声 | 3-4 段 | 简单合成或 Suno 短旋律 |
### 7.2 TTS 方案:VoxCPM2 本地声音克隆
所有角色语音使用 **VoxCPM2**(本地部署,无需外部 API),通过声音克隆实现一致的角色音色。
**服务启动**
```bash
bash /home/xsl/AutoVideo/start-voxcpm.sh
# uvicorn 服务: http://127.0.0.1:8000
```
**API 接口**
- `POST /v1/voices` — 注册参考音频(wav_base64)→ 返回 voice_id
- `POST /v1/speech` — 合成语音(text + voice_id)→ 返回 WAV
- 输出格式:48kHz 16-bit PCM WAV
- 参数:cfg_value(默认 2.0)、inference_timesteps(默认 10
**声音克隆流程**
```
参考音频 (voice/{角色}/*.wav)
→ 转换为 16kHz mono WAV
→ POST /v1/voices 注册 → voice_id
→ POST /v1/speech 合成台词 → WAV
→ ffmpeg atempo 调速 → 最终音频
```
### 7.3 角色音色配置
| 角色 | 参考音频目录 | 语速 | 音色描述 |
|------|-------------|------|---------|
| **林夏** | `voice/linxia/` | 1.00 | 24 岁女生,疲惫但清醒,克制但有温度 |
| 妈妈 | `voice/mom/` | 0.92 | 50 上下,温柔不黏腻,西部口音轻微 |
| 阿哲 | `voice/azhe/` | 1.05 | 27 岁,城市男生,理性偏冷 |
| 小美 | `voice/xiaomei/` | 1.10 | 24 岁,活泼,语速快 |
| 小美(醉) | `voice/xiaomei/` | 0.95 | 同音色,降速模拟醉态 |
| 周南 | `voice/zhounan/` | 0.95 | 26 岁,温和略拘谨 |
| HR 王姐 | `voice/hr/` | 1.00 | 38 岁,职场化,疲惫的善意 |
| 安安 | `voice/anan/` | 1.15 | 23 岁,活泼,比小美更嗲 |
| 室友 A | `voice/dorm_a/` | 1.10 | 年轻女声,兴奋 |
| 室友 B | `voice/dorm_b/` | 1.00 | 年轻女声,随和 |
| 室友 C | `voice/dorm_c/` | 0.95 | 年轻女声,温柔 |
| 外卖小哥 | `voice/delivery/` | 1.20 | 30 岁,麻利 |
| self-3y | `voice/linxia/` | 1.15 | 3 年前的林夏,更明朗(同一音色,不同语速) |
### 7.4 批量合成脚本
```bash
# 启动 VoxCPM2 服务
bash /home/xsl/AutoVideo/start-voxcpm.sh
# 使用 WAV 参考音色,批量合成所有台词
python audio/batch_tts_voxcpm.py --source voice
# 只合成特定角色
python audio/batch_tts_voxcpm.py --source voice --role linxia
# 干跑查看任务列表
python audio/batch_tts_voxcpm.py --source voice --dry-run
```
### 7.5 UI 音效(Web Audio 合成,无需文件)
所有 UI 音效通过 `SFX_UI` 对象在浏览器端实时合成:
| 音效 | 用途 | 实现方式 |
|------|------|---------|
| `tap` | 单击确认 | 880Hz, 45ms |
| `swipe` | 滑动确认 | 660Hz, 60ms |
| `error` | 无效操作 | 200Hz square, 100ms |
| `notify` | 新消息到达 | 523Hz + 659Hz 双音 |
| `choiceReady` | 进入选择 | 440→523→659 三音上行 |
| `choiceWarm` | 右滑温暖 | 440→659Hz 上行 |
| `choiceCold` | 左滑冷淡 | 523→349Hz 下行 |
| `choiceCurious` | 上滑追问 | 523→784Hz 快速上行 |
| `choiceSilent` | 下滑沉默 | 330Hz 极轻 |
| `typing` | 打字中 | 6 次随机频率短脉冲 |
| `send` | 发送 | 400→900Hz 上扫 |
| `ending` | 结局 | 三音渐弱 |
### 7.6 BGM 与环境音
| 曲目 | 用途 | 描述 |
|------|------|------|
| M1 | 全程低氛围 Loop | lo-fi midnight bedroom, soft piano, light vinyl noise |
| M2 | MSG-08/09 悬疑 | subtle ambient suspense, low drone |
| M3 | MSG-13 周南段落 | gentle indie folk, fingerpicked guitar |
| M4 | MSG-16 结尾 | warm cinematic piano, single melody line |
环境音根据 PROFILE 累计分数动态调整:
- engagement 倾向:柔和雨声、远处隐约的音乐
- avoidance 倾向:安静、时钟滴答、远处车声
- nostalgia 倾向:梦幻混响、远处风铃
---
## 8. 技术实现
### 8.1 技术栈
纯前端 Web 应用(单 HTML + JS),无需后端服务器:
- **手势识别**`gestures.js` — 6 种手势(单击、双击、四方向滑动)
- **音频管理**`audio.js` — Web Audio API 合成 + HTML5 Audio 播放
- **触觉反馈**`haptics.js` — Vibration API
- **剧情数据**`story.js` — 16 条消息的完整对话树
- **游戏引擎**`engine.js` — 状态机 + 异步对话流程
- **行为画像**`profile.js` — PROFILE 计分 + 状态变量
### 8.2 状态机
```
idle → notification/ringing → playing → choosing → responding → reacting → [choosing | idle] → ending
```
| 状态 | 说明 | 可用手势 |
|------|------|---------|
| idle | 等待消息 | — |
| notification | 消息到达提示中 | 单击(播放)、双击(跳过) |
| ringing | 来电铃声中 | 单击(接听)、左滑(拒接) |
| playing | NPC 语音播放中 | 双击(跳过语音) |
| choosing | 等待玩家选择 | 右滑、左滑、上滑、下滑、单击 |
| responding | 林夏语音播放中 | 双击(跳过) |
| reacting | NPC 反应播放中 | 双击(跳过) |
| ending | 结局播放中 | — |
### 8.3 异步对话流程
```javascript
async _handleChoice(dir) {
const choice = currentMsg.choices[dir];
// 1. 情感音效
SFX_UI[emotionSfx[dir]]();
// 2. 打字 → 发送
state = 'responding';
SFX_UI.typing();
await sleep(800);
SFX_UI.send();
await sleep(300);
// 3. 林夏的回应
if (choice.linxia) {
await playVoiceAsync([choice.linxia]);
} else {
await sleep(1500); // 沉默
}
// 4. NPC 反应
if (choice.npcReact) {
state = 'reacting';
await playVoiceAsync(choice.npcReact);
}
// 5. 后续对话(Round 3
if (choice.followUp) {
state = 'playing';
await playVoiceAsync(choice.followUp.audio);
if (choice.followUp.choices) {
enterChoosing(choice.followUp.choices);
return;
}
}
// 6. 推进到下一条
proceedToNext();
}
```
### 8.4 跳过机制
`responding``reacting` 状态中双击:
- 设置 `_skipRequested = true`
- 中断当前语音播放
- 异步函数在每个步骤检查 skip 标志,快速跳到下一阶段
### 8.5 隐私承诺
本作**不申请任何敏感权限**
- ❌ 不要麦克风、摄像头、位置、通讯录
- ❌ 不需要联网(全程离线可玩)
- ✅ 仅使用:触觉震动 + 触控输入 + 系统音量
- 玩家行为数据只在本地存储,从不上传。
---
## 9. 音频资产清单
### 林夏语音
| 类别 | 数量 | 平均时长 | 总时长 |
|------|------|---------|-------|
| 通用短句(嗯、好、知道了等) | ~10 条 | 1s | ~10s |
| 消息专属台词 | ~45 条 | 2s | ~90s |
| **合计** | **~55 条** | | **~100s** |
### NPC 语音(Round 1 主台词 + Round 2/3 反应)
| 角色 | Round 1 | Round 2/3 | 总条数 | 总时长 |
|------|---------|-----------|-------|-------|
| 妈妈 | 11 段 | 10 条 | ~21 条 | ~340s |
| 小美 | 7 段 | 10 条 | ~17 条 | ~120s |
| 阿哲 | 2 段 | 8 条 | ~10 条 | ~50s |
| 周南 | 5 段 | 10 条 | ~15 条 | ~80s |
| HR 王姐 | 1 段 | 3 条 | ~4 条 | ~20s |
| 安安 | 1 段 | 5 条 | ~6 条 | ~20s |
| 寝室群 | 5 段 | 8 条 | ~13 条 | ~50s |
| 外卖小哥 | 1 段 | 0 | ~1 条 | ~5s |
| self-3y | 3 版 | 0 | ~3 条 | ~150s |
| **合计** | | | **~90 条** | **~835s** |
### 总计
- 林夏 ~55 条,~100 秒
- NPC ~90 条,~835 秒
- **总语音约 145 条,约 15.5 分钟**
- UI 音效:12 种,全部 Web Audio 合成
- BGM4 段
- 环境音:4-5 段
---
## 10. 项目文件结构
```
blind/
├── game/
│ ├── index.html # 入口
│ └── js/
│ ├── main.js # 启动 + 事件绑定
│ ├── gestures.js # 6 种手势识别
│ ├── haptics.js # 触觉反馈
│ ├── audio.js # 音频管理 + UI 合成音效
│ ├── story.js # 16 条消息剧情数据
│ ├── engine.js # 游戏引擎(状态机 + 对话流程)
│ ├── profile.js # 行为画像 + 状态变量
│ └── utils.js # 工具函数(sleep, dbg 等)
├── audio/
│ ├── mp3/
│ │ ├── linxia/ # 林夏语音(当前 edge-tts 版本)
│ │ └── react/ # NPC 反应语音(当前 edge-tts 版本)
│ ├── 00_raw/tts/ # VoxCPM2 生成原始 WAV
│ └── batch_tts_voxcpm.py # VoxCPM2 批量合成脚本
├── voice/ # VoxCPM2 参考音色(WAV
│ ├── linxia/ # 林夏
│ ├── mom/ # 妈妈
│ ├── azhe/ # 阿哲
│ ├── xiaomei/ # 小美
│ ├── zhounan/ # 周南
│ ├── hr/ # HR 王姐
│ ├── anan/ # 安安
│ ├── dorm_a/ # 室友 A
│ ├── dorm_b/ # 室友 B
│ ├── dorm_c/ # 室友 C
│ └── delivery/ # 外卖小哥
├── gdd/
│ └── redesign_v2.md # v2 重构设计文档
└── 林夏.md # 本文档(总设计纲要)
```
---
## 11. 风险与对策
| 风险 | 对策 |
|------|------|
| VoxCPM2 长情绪戏(妈妈 4 分钟)质感不够 | 分段录制 + 后期拼接,预留调参时间 |
| 声音克隆音色不够区分 | 每角色选择差异大的参考音频 + 调速区分 |
| 60+ 分支语音管理复杂 | batch_tts_voxcpm.py 统一管理,命名规范 |
| 手势误判导致选错 | 滑动阈值 30px + 40°范围;双击间隔 250ms 内 |
| 玩家不理解手势含义 | MSG-02(小美教学关)强引导,每步正向反馈 |
| 女性玩家代入感 | 编剧组需有 24 岁左右女性参与 |
| 题材敏感 | 开场加心理预警 + 提供试玩版 |
---
## 12. 制作流程
| 阶段 | 关键交付 |
|------|---------|
| **剧本定稿** | 16 条全脚本 + 5 结局完整对白 + 所有分支台词 |
| **参考音色准备** | voice/ 下 12 个角色参考音频确认 |
| **VoxCPM2 批量合成** | ~145 条语音 + 语速调整 + 质检 |
| **BGM + 环境音** | 4 段 BGM + 环境采样 |
| **程序集成** | story.js 对接最终音频路径 + 全流程调试 |
| **测试** | 全 5 结局通关测试 + 分支覆盖 |
---
## 附录 · 一句话给主创
> "这不是一个让女孩们更难过的游戏。这是一个让她们在难过的时候,知道有人也曾这样难过、并且活下来的游戏。"