Files
blind/other_docs/林夏_原始设计.md
T
xsl 866f363591 项目整理:重写主文档、归档过时文档、清理死代码、新增音频制作手册
文档整理:
- 重写 game/操作说明.md 为项目主文档(6游戏/6手势/旁白/模型生成音频/TTS两步流程)
- 归档过时文档至 other_docs/(林夏原始设计、旧执行文档)
- 新增 docs/音频制作执行手册.md(从零制作游戏音频的完整流程)
- docs/ 下保留旁白音色提示词、音频脚本

代码清理:
- 删除旧架构死代码: story.js / gestures.js / profile.js
- 删除耦合旧架构的过时测试目录 game/tests/

脚本配置修正:
- check_tts_env.sh: 启动脚本路径 AutoVideo → tts-server(实际位置)
- gen_narrator_samples.py: 硬编码地址改为环境变量,默认对齐 tts-server 端口 8000
2026-06-19 22:29:04 +08:00

943 lines
48 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 |
| **长按(1s** | 根据状态:重听或播报 | 确认 | 缓慢长震 | 低沉确认音 330→440Hz |
| **右滑** | 温暖 / 靠近 | 接纳、关心 | 流畅长震 | 上行音 440→659Hz |
| **左滑** | 冷淡 / 推开 | 疏离、敷衍 | 干脆短震 | 下行音 523→349Hz |
| **上滑** | 好奇 / 追问 | 想了解更多 | 快速上弹震 | 快速上行 523→784Hz |
| **下滑** | 沉默 / 退缩 | 说不出口 | 极轻慢震 | 极轻低音 330Hz |
> **不需要记位置**。每个方向有固定的情感含义,贯穿全游戏。右滑永远是温暖,左滑永远是冷淡。
>
> **长按随时可用**:任何状态下长按 1 秒,系统根据当前状态做出响应——在 `playing` 或 `choosing` 状态下重播上一句 NPC 语音,其他状态下语音播报当前位置(例如"小美的消息,等待回复")。这是玩家在纯音频环境下的"安全网"。
### 2.2 对话流程(多轮交互)
```
┌─────────────────────────────────────────────────┐
│ [提示音 + 震动] │
│ 消息到达 │
│ → 单击播放 / 双击跳过 │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ NPC 语音播放(Round 1
│ "你被甩了我都听说了,明天陪你喝酒" │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ ★ 进入选择态 │
│ [三音上行提示音 + 明确震动模式(长-短-长)] │
│ 系统语音提示:"等待回复" │
│ 玩家操作:右滑 / 左滑 / 上滑 / 下滑 / 单击 │
│ 长按 → 重听 NPC 刚才说的话 │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ [打字音效 ~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-16 妈妈长语音第 5 段中提到"我外婆每天晚上都给我打一个电话,她不说什么"——暗示未知号码就是妈妈从另一个号码拨出的"不说什么的电话",与妈妈线形成跨代呼应。不做明确揭示,留给玩家自己领悟。如果玩家接了 MSG-08 + 听完 MSG-16,两段呼吸/沉默会在记忆中形成对位。
---
### 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**:消息到达提示音。系统语音朗读三个字——"睡了吗?"
| 方向 | 林夏说 | 妈妈反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "没有,怎么了?" | 妈妈发语音:"没事,就是想起来问一下。今天怎么样?" | 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 |
**上滑 Round 3**(阿哲说"就 5 分钟"后,再次进入选择):
- 右滑 → 林夏:"……来吧。" → 阿哲:"谢谢。我马上到。" → [挂断] → HE_BACK=true
- 左滑 → 林夏:"还是算了。" → 阿哲:"好吧。" → [挂断] → 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
- 左滑 → 林夏:"嗯,没事。" → 妈妈:"……好吧。晚安。" → MOM_LINK=true
- 下滑 → [沉默] → 妈妈:"你不说话我更担心。算了,不逼你了。晚安。" → 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. "我想跟你说,那年夏天我没死,是因为我外婆每天晚上都给我打一个电话。"
- **音频设计**:第 5 段开头,叠入与 MSG-08 相同的电话底噪质感(轻微线路嘶声),持续 2-3 秒后淡出。如果玩家之前接了 MSG-08 的未知号码,这一刻会在听觉上产生似曾相识的感觉——同样的电话底噪、同样的呼吸停顿,但这次有了妈妈的声音。
6. "她不说什么,就问我今天吃饭了没。"
- **音频设计**"不说什么"之后留 3 秒静默(仅电话底噪),呼应 MSG-08 的 6 秒呼吸声,形成跨消息的听觉对位。
7. "我现在打给你,你不接也没事。妈妈给你发个语音,你什么时候想听就听。你不用回我。"
8. "你今天接了好多电话吧。妈妈不打扰你了。晚安。"
*无选择交互。让玩家安静听完。播放结束 → 触发结局判定。*
**如果 MOM_LINK=false**MSG-16 不播放。MSG-15 结束后,静默 5 秒(环境底噪),然后直接进入结局判定。此时结局 A 的条件不可能满足(缺少 MOM_LINK),流程会落入结局 B/C/D。
---
## 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 版本 + 环境音 |
| **UNREAD_COUNT** | 未播放/未接听的消息数(初始 16,每播放或接听 −1) | 决定结局 C/D |
| **REPLY_COUNT** | 林夏实际发出回应的消息条数 | 决定结局 C |
**REPLY_COUNT 计数规则**
- 选择右滑/左滑/上滑/单击(发出语音回应)→ +1
- 下滑(沉默)→ 不计
- 电话接听后自动回应(MSG-01/03/14)→ +1
- 双击跳过整条消息 → 不计
- 发表情(MSG-07 单击)→ +1
### 变量影响层级
并非所有选择都对结局产生等量影响。设计上区分**核心变量**与**氛围变量**:
| 层级 | 变量 | 影响 | 相关消息 |
|------|------|------|---------|
| **核心** | MOM_LINK | 决定 MSG-16 是否播放(妈妈长独白),直接影响结局 A | MSG-01, 10, 14 |
| **核心** | HE_BACK | 决定门铃场景是否触发,区分结局 A/B | MSG-11 |
| **核心** | ZHOUNAN_DEPTH + ZHOUNAN_SHARE | 决定隐藏结局 E | MSG-12, 13 |
| **辅助** | SELF_RECORD | 决定结局 A 是否可达 | MSG-15 |
| **氛围** | GROUP_REPLY | 影响彩蛋消息,但不改变结局走向 | MSG-07 |
| **氛围** | PROFILEavoidance/engagement/nostalgia | 影响环境音色调 + self-3y 版本,不直接决定结局 | 全局累计 |
| **统计** | UNREAD_COUNT / REPLY_COUNT | 决定结局 C/D 的触发条件 | 全局自动累计 |
> **设计原则**:HR、安安、寝室群、小美醉酒等消息的选择主要影响 PROFILE 氛围和当下的对话反馈,让玩家"听得出这一句不一样"。真正改变结局走向的是**妈妈线、阿哲线、周南线**三条核心关系。玩家在这三条线上的选择会产生明确的、跨消息的后果。
### 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 条
**优先级**2
- 妈妈语音播完。林夏起身,环境声:走到厨房、烧水、坐在小板凳上。
- 林夏轻声自语:"明天再说。"
- 烧水壶滴的一声 → 游戏结束。
### 结局 B · "AirPods 我寄给你"
**触发**HE_BACK=false + 回过周南至少 1 条
**优先级**3
- 林夏给阿哲发语音"我寄给你,地址发我"(打字音效 → 发送音效 → 林夏语音播放)。
- 林夏轻声自语:"寄出去了。"
- 一个"清醒地走"的结局。
### 结局 C · "全部已读"
**触发**UNREAD_COUNT=0 且 REPLY_COUNT≤2
**优先级**4
- 系统语音播报实际数值:"今晚共 16 条新消息,已读 {16UNREAD_COUNT},未回复 {16REPLY_COUNT}。"
- 长沉默。林夏轻声自语:"今晚就这样。"
### 结局 D · "明天再开机"
**触发**UNREAD_COUNT≥8**或**结局 E/A/B/C 均不满足(真正兜底)
**优先级**5(兜底结局)
- 系统语音提示电量 1%。触感震动 + 白噪 → 彻底黑。
- 第二天清晨鸟叫。林夏轻声自语:"早上好。"
### 结局 E · 隐藏 · "周南,我也记得你"
**触发**ZHOUNAN_DEPTH≥2 + ZHOUNAN_SHARE + PROFILE=engagement 或 nostalgia
**优先级**1(最高优先级)
- 周南秒回:"那我下次出差来北京,请你吃饭好不好。"
- 系统播放林夏的轻笑——这是全游戏唯一一次林夏主动笑出声。
- 林夏轻声说:"好。"
### 结局判定规则
当玩家同时满足多个结局条件时,按**优先级数字从小到大**判定,取第一个满足的结局:
```
E(隐藏,优先级 1)→ A(优先级 2)→ B(优先级 3)→ C(优先级 4)→ D(兜底,优先级 5)
```
- 结局 E 最稀有、最难触发,一旦满足则覆盖其他结局。
- 结局 D 是**真正的兜底结局**——无论玩家怎么玩,只要 E/A/B/C 都不满足就一定进入 D。UNREAD_COUNT≥8 是 D 的常见触发场景,但不是唯一入口。
- 结局 A 和 B 可能同时满足(例如 MOM_LINK=true + SELF_RECORD=true + HE_BACK=false + 回过周南),此时优先触发 A,因为 A 是更完整的情感闭合。
---
## 7. 音频设计
### 7.1 音频资产分类
| 类别 | 说明 | 数量 | 制作方案 |
|------|------|------|---------|
| **林夏语音** | 主角对话回应(通用 + 专属) | ~55 条, ~100s | VoxCPM2 声音克隆 |
| **NPC Round 1 语音** | 各角色主要台词 | ~30 条, ~300s | VoxCPM2 声音克隆 |
| **NPC Round 2/3 反应** | 对话分支反应 | ~54 条, ~205s | VoxCPM2 声音克隆 |
| **self-3y 三版** | 时光胶囊备忘录 ×3 | 3 条, ~150s | VoxCPM2`self_3y_v03` 独立参考) |
| **UI 短音效** | 提示音、选择反馈、打字/发送 | ~12 种 | Web Audio 合成(无需文件) |
| **BGM** | 4 段主题音乐 | 4 轨 | ACE-Step 1.5 本地生成 |
| **环境音** | 卧室夜噪、厨房、黎明等 | 3 段 | ACE-Step 1.5 本地生成 |
| **角色铃声** | 来电铃声 | 5 条(游戏引用 4 种场景) | ACE-Step 1.5 本地生成 |
| **叙事音效** | 敲门、心跳、呼吸、水壶等 | 4 条 | ACE-Step 1.5 本地生成 |
### 7.2 TTS 方案:VoxCPM2 本地克隆(现行 v5 链路)
> **注意**:本项目 TTS 统一使用 **VoxCPM2**。CosyVoice2 仅作备用,不作为主生产链路。
> **详细操作与目录**:见 `audio_2/项目状态说明.md`、`执行文档_音频素材制作.md`。
**现行流程**2026-05 落地):
```
Voice Design 试听(v4,每角色 5 条提示词变体)
→ 人工选定参考 wav
→ Style Control 克隆 + 表演修饰(v5
→ 部署到 audio/mp3/tts/*_wav_v1.mp3(游戏直接加载)
```
**服务启动**(端口 **8002**8000 被其他服务占用):
```bash
VOXCPM_PORT=8002 VOXCPM_ENABLE_DENOISER=0 bash /home/xsl/tts-server/start-voxcpm.sh
curl http://127.0.0.1:8002/health
# 预期: {"status":"ok","model":"VoxCPM2","styled_speech":true}
```
**API 接口**
| 接口 | 用途 |
|------|------|
| `POST /v1/voices` | 注册参考音频(wav_base64)→ voice_id |
| `POST /v1/speech` | 纯克隆合成(text + voice_id |
| `POST /v1/speech/styled` | Style Control:有 voice_id = 克隆+表演;无 voice_id = Voice Design |
- 输出格式:48kHz 16-bit PCM WAV
- 参数:cfg_value(默认 2.0)、inference_timesteps(默认 10
**批量生产(现行)**
```bash
# 全量克隆并部署游戏目录
VOXCPM_PORT=8002 python audio_2/scripts/04_gen_tts_clone.py --force
# 只重跑某角色
VOXCPM_PORT=8002 python audio_2/scripts/04_gen_tts_clone.py --role mom --force
# 重新生成试听样本(换提示词挑音色)
VOXCPM_PORT=8002 python audio_2/scripts/03_gen_voice_samples.py --role delivery --force
```
**游戏音频命名**:仅 `*_wav_v1.mp3``story.js` / `engine.js` 引用;`*_mp3_v1.mp3` 为旧流水线产物,已清理。
### 7.3 角色音色配置
参考音频来自 Voice Design 试听选定,存放在 `voice/{角色}/{角色}_ref.wav``audio_2/tts/v4_voice_samples/{角色}/`
| 角色 | 参考文件 | 目录 | 语速 | 音色描述 |
|------|----------|------|------|---------|
| **林夏** | `linxia_v05.wav` | `voice/linxia/` | 1.00 | 24 岁女生,疲惫但清醒,克制但有温度 |
| 妈妈 | `mom_v01.wav` | `voice/mom/` | 0.92 | 50 上下,温柔不黏腻,西部口音轻微 |
| 阿哲 | `azhe_v03.wav` | `voice/azhe/` | 1.05 | 27 岁男声,城市男生,理性偏冷 |
| 小美 | `xiaomei_v05.wav` | `voice/xiaomei/` | 1.10 | 24 岁,活泼,语速快 |
| 小美(醉) | `xiaomei_drunk_v05.wav` | `voice/xiaomei/` | 0.95 | 同闺蜜基底,降速模拟醉态 |
| 周南 | `zhounan_v05.wav` | `voice/zhounan/` | 0.95 | 26 岁男声,温和略拘谨 |
| HR 王姐 | `hr_v05.wav` | `voice/hr/` | 1.00 | 38 岁,职场化,疲惫的善意 |
| 安安 | `anan_v04.wav` | `voice/anan/` | 1.15 | 23 岁,活泼,比小美更嗲 |
| 室友 A | `dorm_a_v03.wav` | `voice/dorm_a/` | 1.10 | 年轻女声,兴奋 |
| 室友 B | `dorm_b_v02.wav` | `voice/dorm_b/` | 1.00 | 年轻女声,随和 |
| 室友 C | `dorm_c_v03.wav` | `voice/dorm_c/` | 0.95 | 年轻女声,温柔 |
| 外卖小哥 | `delivery_v04.wav` | `voice/delivery/` | **1.00** | 30 岁**男声**,北方腔,语速适中 |
| self-3y | `self_3y_v03.wav` | `voice/linxia/` | 1.15 | 3 年前的林夏,更年轻明亮(独立参考,非复用小美) |
表演修饰(warm/cold/分段等)由 `audio_2/voice_style_prompts.py``get_clone_style()` 控制;音色本身由参考 wav 锚定。
### 7.4 脚本与产物目录
| 脚本 | 用途 | 输出 |
|------|------|------|
| `audio_2/scripts/03_gen_voice_samples.py` | v4 试听(每角色 5 条) | `audio_2/tts/v4_voice_samples/` |
| `audio_2/scripts/04_gen_tts_clone.py` | **v5 全量克隆 + 部署** ★ | `audio_2/tts/v5_cloned/*_wav_v5.mp3``audio/mp3/tts/*_wav_v1.mp3` |
| `audio/batch_tts_voxcpm.py` | 台词表 `LINES`(173 条)+ 语速配置 | 旧版直连克隆,已被 v5 替代 |
| `audio_2/voice_style_prompts.py` | 表演修饰 / Voice Design 提示词 | — |
| `audio_2/voice_sample_prompts.py` | v4 试听变体与统一试听文本 | — |
**台词表**`audio/batch_tts_voxcpm.py``LINES` + `04_gen_tts_clone.py``EXTRA_LINES`self-3y ×3、系统 memo)。
**配置与文档**
```bash
# 旧版批量(仅参考,不再用于正式产出)
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 上扫 |
| `longPress` | 长按确认 | 330→440Hz 缓升 |
| `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` — 7 种手势(单击、双击、长按、四方向滑动)
- **音频管理**`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 | 等待玩家选择 | 右滑、左滑、上滑、下滑、单击、长按(重听 NPC 上一句) | **三音上行 + 长-短-长震动 + "等待回复"语音** |
| responding | 林夏语音播放中 | 双击(跳过) | 打字音效 |
| reacting | NPC 反应播放中 | 双击(跳过) | — |
| ending | 结局播放中 | 三指点击(退出) | 结局专属音效 |
> **关键**`playing → choosing` 的状态切换必须有**明确的听觉分界**(三音上行 + 震动模式变化 + "等待回复"语音提示),确保玩家在纯音频环境下不会误以为自己还在听消息。单击在 `notification` 状态是"播放",在 `choosing` 状态是"嗯"——两者之间有明确的进入选择态提示,不会混淆。
### 8.3 异步对话流程
```javascript
// 进入选择态时的明确确认
function enterChoosing(choices) {
state = 'choosing';
SFX_UI.choiceReady(); // 三音上行
haptics.pattern([200, 100, 80, 100, 200]); // 长-短-长 震动
speak('等待回复'); // 系统语音确认
currentChoices = choices;
}
// 长按处理:任何状态下可用
async _handleLongPress() {
SFX_UI.longPress();
if (state === 'choosing' || state === 'playing') {
// 重播上一句 NPC 语音
await playVoiceAsync([lastNpcAudio]);
} else {
// 播报当前状态
speak(getStatusText()); // 如:"小美的消息,等待回复"
}
}
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 全局安全手势
除 §2.1 的 7 种剧情手势外,有两种**全局安全手势**,它们的优先级高于任何剧情手势,在所有状态下均可触发:
| 手势 | 功能 | 优先级 |
|------|------|--------|
| **两指长按(1s** | 暂停游戏 | 最高——立即中断当前音频播放,进入暂停状态 |
| **三指点击** | 安全退出 | 最高——系统语音确认后跳转到支持资源页 |
**实现要求**
- `gestures.js` 在手势识别时先判断触摸点数量:≥3 → 安全退出,=2 且持续 ≥1s → 暂停,=1 → 进入剧情手势判断。
- 暂停状态下仅响应:单击(继续)、三指点击(退出)。
- 安全手势不影响任何游戏变量(PROFILE、REPLY_COUNT 等不变)。
### 8.6 触觉降级策略
触觉反馈(Vibration API)是增强体验,**不是必需通道**。iPhone Safari 不支持 Vibration API,部分 Android 浏览器也可能受限。设计原则:
- **所有状态切换必须有独立的音效/语音确认**,不能仅靠震动区分。
- `haptics.js` 在初始化时检测 `navigator.vibrate` 是否可用,不可用时静默降级。
- 测试时必须在关闭震动的情况下完整通关一遍,确认纯音频也能正常游玩。
### 8.6 隐私承诺
本作**不申请任何敏感权限**
- ❌ 不要麦克风、摄像头、位置、通讯录
- ❌ 不需要联网(全程离线可玩)
- ✅ 仅使用:触觉震动 + 触控输入 + 系统音量
- 玩家行为数据只在本地存储,从不上传。
---
## 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** |
### 总计(现行部署)
- 游戏引用 **184** 个 MP3`audio/mp3/`,已与 `story.js` / `engine.js` 对齐)
- TTS 对白 **169** 条(`tts/*_wav_v1.mp3`
- BGM + 铃声 **8** 条 · 音效 **4** 条 · 环境音 **3**
- v5 克隆产物归档 **177** 条(`audio_2/tts/v5_cloned/`,含台词表全量 + self-3y + 系统 memo
- UI 音效:13 种,全部 Web Audio 合成
---
## 10. 项目文件结构
```
blind/
├── game/
│ ├── index.html
│ └── js/
│ ├── story.js # 16 条消息 + 音频引用(*_wav_v1.mp3
│ ├── engine.js # 系统语音、结局、BGM/环境音
│ ├── audio.js # 音频管理 + UI 合成音效
│ └── …
├── audio/
│ ├── batch_tts_voxcpm.py # 台词表 LINES + ROLES 语速
│ └── mp3/ # 游戏运行时音频(184 文件)
│ ├── tts/ # 169 条对白
│ ├── music/ # BGM + 铃声
│ ├── sfx/ # 叙事音效
│ └── ambience/ # 环境音
├── voice/ # 各角色参考音频(v5 选定 *_ref.wav
├── audio_2/ # TTS v4/v5 流水线
│ ├── 项目状态说明.md
│ ├── voice_style_prompts.py
│ ├── voice_sample_prompts.py
│ ├── scripts/ # 03 试听 · 04 克隆部署 ★
│ └── tts/
│ ├── v4_voice_samples/ # 选定参考音色
│ └── v5_cloned/ # 正式产物 + clone_manifest.tsv
├── 林夏.md
├── 执行文档_音频素材制作.md
└── 技术报告_音频资源制作.md
```
TTS 服务(独立部署):`/home/xsl/tts-server/`(端口 **8002**
---
## 11. 风险与对策
| 风险 | 对策 |
|------|------|
| VoxCPM2 长情绪戏(妈妈 4 分钟)质感不够 | 分段录制 + 后期拼接,预留调参时间 |
| 声音克隆音色不够区分 | Voice Design 试听多变体 → 人工选定 → 克隆锚定;男声角色单独标注 |
| 60+ 分支语音管理复杂 | batch_tts_voxcpm.py 统一管理,命名规范 |
| 手势误判导致选错 | 滑动阈值 30px + 40°范围;双击间隔 250ms 内 |
| 玩家不理解手势含义 | MSG-02(小美教学关)强引导,每步正向反馈 |
| 女性玩家代入感 | 编剧组需有 24 岁左右女性参与 |
| 题材敏感 | 见下方"情绪安全设计"专节 |
### 情绪安全设计
本作涉及分手、裁员、独居、深夜来电、家庭创伤回忆等高情绪浓度内容,必须认真对待玩家的心理安全。
**开场内容提示**(游戏启动后、第一条消息前):
- 系统语音播报:"本游戏涉及情感压力、职场失落、家庭关系等主题。如果你当前情绪状态不佳,建议择日再玩。游戏过程中,两指长按可暂停,三指点击可直接退出。"
**游戏中安全机制**
- **随时暂停**:任何状态下两指同时长按 1 秒,游戏暂停,系统语音:"游戏已暂停。单击继续,三指点击退出。"(两指长按与单指双击完全不同,避免误触跳过)
- **安全退出**:三指同时点击,系统语音确认后直接跳转到结尾支持资源页。
- **长按状态播报**:让玩家随时确认自己在哪,减少焦虑感。
**结尾支持资源**(任何结局播放完毕后):
- 系统语音:"感谢你陪林夏度过这一夜。如果你现在需要和人说说话:全国 24 小时心理援助热线 400-161-9995;北京心理危机研究与干预中心 010-8295-1332。"
- 音频播放完毕后保持在屏幕上(即使是纯音频游戏,此处允许显示文字——用户此时可以看手机了,屏幕"修好了"是一个隐喻)。
**叙事安全原则**
- 林夏的自救不完全寄托在妈妈或男性角色身上——妈妈提供的是"被记住"的感觉,周南提供的是"被看见"的感觉,但最终让林夏起身烧水、给阿哲寄耳机的,是她自己的选择。
- MSG-16 妈妈语音中"那年夏天我没死"这句表述需谨慎处理:妈妈的语气是平静追忆而非戏剧化,TTS 合成时 emotion 设为 `gentle, peaceful`,避免制造突然的情绪冲击。
---
## 12. 制作流程
| 阶段 | 关键交付 | 状态 |
|------|---------|------|
| **剧本定稿** | 16 条全脚本 + 5 结局完整对白 + 所有分支台词 | ✅ |
| **参考音色准备** | v4 试听选定 13 角色参考 wav | ✅ |
| **VoxCPM2 v5 批量合成** | 177 条克隆 + 部署 `audio/mp3/tts/` | ✅ |
| **BGM + 环境音 + 音效** | ACE-Step 本地生成,游戏引用 15 条 | ✅ |
| **程序集成** | story.js 对接 `*_wav_v1.mp3` | ✅ |
| **测试** | 全 5 结局通关 + 各角色听感迭代 | 待 playtest |
---
## 附录 · 一句话给主创
> "这不是一个让女孩们更难过的游戏。这是一个让她们在难过的时候,知道有人也曾这样难过、并且活下来的游戏。"