v2收尾:手势升级 + 暂停退出 + 结局语音化 + 分支补全 + 12条新音频

gestures.js: 新增长按(1s)、两指长按(暂停)、三指点击(退出),支持多指检测
engine.js: REPLY_COUNT变量、长按重播/状态广播、暂停恢复退出机制、
  选择提示音、结局台词预录音频替换TTS、内容预警+心理热线
story.js: MSG-10/11 follow-up补全down(沉默)选项
index.html: 操作指南添加长按/安全手势、内容预警文字
batch_tts_voxcpm.py: 新增12条台词(结局旁白+系统提示+分支NPC反应)
audio/mp3/tts: 12个VoxCPM2生成的林夏声线MP3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
xsl
2026-05-19 23:22:04 +08:00
co-authored by Claude Opus 4.6
parent e4d1141b4f
commit d03bcb14e4
21 changed files with 929 additions and 326 deletions
+173 -40
View File
@@ -10,7 +10,7 @@
## 0. 一句话概述
24 岁的林夏,被分手、被裁员、被妈妈催婚,全发生在同一个周五。她把手机调成静音、躲进合租屋,关上灯。屏幕坏了——只剩耳机能听、手指能摸。今晚 16 个人会发来电话和语音。她接谁、不接谁、怎么回——每一个回应都会被对方听到,每一段对话都会走向不同的结局。
24 岁的林夏,被分手、被裁员、被妈妈催婚,全发生在同一个周五。她把手机调成静音、躲进合租屋,关上灯。屏幕坏了——只剩耳机能听、手指能摸。今晚会有 16 电话和语音陆续到达。她接谁、不接谁、怎么回——每一个回应都会被对方听到,每一段对话都会走向不同的结局。
---
@@ -39,18 +39,21 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
## 2. 核心玩法
### 2.1 种手势——零视觉依赖
### 2.1 种手势——零视觉依赖
| 手势 | 全局含义 | 情感色彩 | 触觉反馈 | 音效 |
|------|---------|---------|---------|------|
| **单击** | 接受 / 回应 / 继续 | 中性应答 | 轻触感 | 清脆短音 880Hz |
| **双击** | 跳过 / 忽略 | 不耐烦 / 赶时间 | 短促双震 | 下行滑音 587→392Hz |
| **长按(1s** | 根据状态:重听或播报 | 确认 | 缓慢长震 | 低沉确认音 330→440Hz |
| **右滑** | 温暖 / 靠近 | 接纳、关心 | 流畅长震 | 上行音 440→659Hz |
| **左滑** | 冷淡 / 推开 | 疏离、敷衍 | 干脆短震 | 下行音 523→349Hz |
| **上滑** | 好奇 / 追问 | 想了解更多 | 快速上弹震 | 快速上行 523→784Hz |
| **下滑** | 沉默 / 退缩 | 说不出口 | 极轻慢震 | 极轻低音 330Hz |
> **不需要记位置**。每个方向有固定的情感含义,贯穿全游戏。右滑永远是温暖,左滑永远是冷淡。
>
> **长按随时可用**:任何状态下长按 1 秒,系统根据当前状态做出响应——在 `playing` 或 `choosing` 状态下重播上一句 NPC 语音,其他状态下语音播报当前位置(例如"小美的消息,等待回复")。这是玩家在纯音频环境下的"安全网"。
### 2.2 对话流程(多轮交互)
@@ -67,8 +70,11 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
[三音上行提示音 · 等待选择]
★ 进入选择态
│ [三音上行提示音 + 明确震动模式(长-短-长)] │
│ 系统语音提示:"等待回复" │
│ 玩家操作:右滑 / 左滑 / 上滑 / 下滑 / 单击 │
│ 长按 → 重听 NPC 刚才说的话 │
└──────────┬──────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
@@ -132,7 +138,7 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
| 07 | 寝室群 | 语音 ×5 | 室友 A 升职,@林夏 | 5 方向 + 温暖 Round 3 |
| 08 | 未知号码 | 电话 | 呼吸声 6 秒后挂断 | 无选择(悬疑钩子) |
| 09 | 小美(醉)| 语音 ×4 | "阿哲大三追过我" | 5 方向 + 追问 Round 3 |
| 10 | 妈妈 | 文字 | "睡了吗?" | 5 方向 + 温暖/追问各有 Round 3 |
| 10 | 妈妈 | 文字(系统语音朗读) | "睡了吗?" | 5 方向 + 温暖/追问各有 Round 3 |
| 11 | 阿哲 | 电话 | "我能过去拿吗?就 5 分钟" | 5 方向(关键分支锚点) |
| 12 | 周南 | 语音 | "初三坐你后桌那个" | 5 方向 |
| 13 | 周南 | 语音 ×4 | "今天我生日" | 5 方向 + 温暖 Round 3 |
@@ -269,6 +275,8 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
**无选择**——悬疑钩子。
**不接** → 未接来电。
**回收**:MSG-16 妈妈长语音第 5 段中提到"我外婆每天晚上都给我打一个电话,她不说什么"——暗示未知号码就是妈妈从另一个号码拨出的"不说什么的电话",与妈妈线形成跨代呼应。不做明确揭示,留给玩家自己领悟。如果玩家接了 MSG-08 + 听完 MSG-16,两段呼吸/沉默会在记忆中形成对位。
---
### MSG-09 小美(醉)· 语音 ×4
@@ -298,15 +306,15 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
### MSG-10 妈妈 · 文字消息
**Round 1**:消息到达提示音。TTS 朗读三个字——"睡了吗?"
**Round 1**:消息到达提示音。系统语音朗读三个字——"睡了吗?"
| 方向 | 林夏说 | 妈妈反应 | 效果 |
|------|--------|---------|------|
| 右滑 | "没有,怎么了?" | 妈妈发语音:"没事,就是想起来问一下。今天怎么样?" | MOM_LINK, engagement+2 |
| 左滑 | "嗯,要睡了。" | 妈妈文字:"好,早点睡。" | MOM_LINK, avoidance+1 |
| 左滑 | "嗯,要睡了。" | 系统语音朗读妈妈文字:"好,早点睡。" | MOM_LINK, avoidance+1 |
| 上滑 | "妈……你能打电话给我吗?" | [电话铃响] 妈妈打来了!接听后:"怎么了夏夏?" | MOM_LINK, engagement+3 |
| 下滑 | [不回复] | [30 秒后] 妈妈文字:"看到了回一下。" | avoidance+2 |
| 单击 | "嗯。" | 妈妈文字:"嗯就是睡了?晚安。" | MOM_LINK |
| 下滑 | [不回复] | [30 秒后] 系统语音朗读妈妈文字:"看到了回一下。" | avoidance+2 |
| 单击 | "嗯。" | 系统语音朗读妈妈文字:"嗯就是睡了?晚安。" | MOM_LINK |
**上滑路径**(妈妈打来电话后,说"怎么了夏夏?你声音不对。"):
- 右滑 → 林夏:"没什么……就是有点累。" → 妈妈:"累了就回家歇几天。妈煮了排骨。"
@@ -315,7 +323,9 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
**右滑路径**(妈妈问"今天怎么样"后):
- 右滑 → 林夏:"还好。" → 妈妈:"那就好。别太晚了。"
- 上滑 → 林夏:"不太好。" → 妈妈:"怎么了?" → [可继续展开]
- 上滑 → 林夏:"不太好。" → 妈妈:"怎么了?"
- 右滑 → 林夏:"工作没了。" → 妈妈:"……回来歇几天吧。妈不催你。"
- 下滑 → [沉默] → 妈妈:"不想说就不说。妈在这呢。" → 对话结束
---
@@ -328,10 +338,15 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
|------|--------|---------|------|
| 右滑 | "可以。但只有 5 分钟。" | "好,我马上过来。谢谢。" → [挂断] | HE_BACK=true |
| 左滑 | "不方便。我明天寄给你。" | "好吧……那谢了。" → [挂断] | HE_BACK=false |
| 上滑 | "为什么非要今天来?" | "我……明天要出差。" [停顿] "就 5 分钟。" | 继续选择 |
| 上滑 | "为什么非要今天来?" | "我……明天要出差。" [停顿] "就 5 分钟。" | → 追问后再选 |
| 下滑 | [沉默 → 挂断音] | [电话断了] | HE_BACK=false |
| 单击 | "不了。" | "好。" → [挂断] | HE_BACK=false |
**上滑 Round 3**(阿哲说"就 5 分钟"后,再次进入选择):
- 右滑 → 林夏:"……来吧。" → 阿哲:"谢谢。我马上到。" → [挂断] → HE_BACK=true
- 左滑 → 林夏:"还是算了。" → 阿哲:"好吧。" → [挂断] → HE_BACK=false
- 下滑 → [沉默] → 阿哲:"那……你考虑一下,我等你消息。" → [挂断] → HE_BACK=false
**如果 HE_BACK=true** → 消息 12 之前插入门铃声 + 敲门声场景。
---
@@ -382,8 +397,12 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
接听后 → 林夏自动说"嗯,我没事。"
妈妈:"真的没事?"
- 如果 MOM_LINK=true"好。那早点睡。妈爱你。"
- 如果 MOM_LINK=false"你今天怎么都不理妈妈?出什么事了吗?" → 可选择回应
- 如果 MOM_LINK=true"好。那早点睡。妈爱你。" → 对话结束
- 如果 MOM_LINK=false"你今天怎么都不理妈妈?出什么事了吗?" → 进入选择:
- 右滑 → 林夏:"没事妈,就是手机坏了。" → 妈妈:"那你赶紧修啊。好了早点睡。" → MOM_LINK=true
- 左滑 → 林夏:"嗯,没事。" → 妈妈:"……好吧。晚安。" → MOM_LINK=true
- 下滑 → [沉默] → 妈妈:"你不说话我更担心。算了,不逼你了。晚安。" → MOM_LINK=true
**不接** → MOM_LINK 保持当前值(可能仍为 false)
**状态**:接听 → MOM_LINK=true
@@ -409,12 +428,16 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
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. 分支判定变量
@@ -424,10 +447,35 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
| **MOM_LINK** | 是否接妈妈两次电话中的至少一次 / 是否回复"睡了吗?" | 决定 MSG-16 是否播放 |
| **HE_BACK** | 是否答应阿哲过来拿耳机(MSG-11 右滑) | 决定 MSG-12 前是否插入门铃场景 |
| **GROUP_REPLY** | 是否在寝室群里回复过(MSG-07 非沉默) | 决定彩蛋消息 |
| **ZHOUNAN_DEPTH** | 给周南的回应深度(累计) | 决定结局字幕 + self-3y 版本 |
| **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 计分细则
@@ -450,31 +498,48 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
### 结局 A · "明天就明天吧"
**触发**MOM_LINK=true + SELF_RECORD=true + 至少回了周南或群里 1 条
**优先级**2
- 妈妈语音播完。林夏起身,环境声:走到厨房、烧水、坐在小板凳上。
- 字幕"明天再说。"
- 林夏轻声自语"明天再说。"
- 烧水壶滴的一声 → 游戏结束。
### 结局 B · "AirPods 我寄给你"
**触发**HE_BACK=false + 回过周南至少 1 条
- 林夏给阿哲发了文字"我寄给你,地址发我"。
- 字幕:"寄出。"
**优先级**3
- 林夏给阿哲发语音"我寄给你,地址发我"(打字音效 → 发送音效 → 林夏语音播放)。
- 林夏轻声自语:"寄出去了。"
- 一个"清醒地走"的结局。
### 结局 C · "全部已读"
**触发**未读数=0 且回复≤2 条
- TTS:"今晚共 16 条新消息,已读 16,未回复 14。"
- 长沉默。字幕:"今晚就这样。"
**触发**UNREAD_COUNT=0 且 REPLY_COUNT≤2
**优先级**4
- 系统语音播报实际数值:"今晚共 16 条新消息,已读 {16UNREAD_COUNT},未回复 {16REPLY_COUNT}。"
- 长沉默。林夏轻声自语:"今晚就这样。"
### 结局 D · "明天再开机"
**触发**未读数≥8
- 系统提示电量 1%。触感震动 + 白噪 → 彻底黑。
- 第二天清晨鸟叫。字幕:"早上好。"
**触发**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 是更完整的情感闭合。
---
@@ -493,7 +558,9 @@ NPC 说话 → 玩家选择方向 → 林夏说出回应 → NPC 对林夏的回
| **环境音** | 卧室夜噪、酒吧、街声 | 4-5 段 | Freesound (CC0/CC-BY) |
| **角色铃声** | 来电铃声 | 3-4 段 | 简单合成或 Suno 短旋律 |
### 7.2 TTS 方案:VoxCPM2 本地声音克隆
### 7.2 TTS 方案:VoxCPM2 本地声音克隆(唯一主链路)
> **注意**:本项目 TTS 统一使用 **VoxCPM2**。`执行文档_音频素材制作.md` 中早期记录的 CosyVoice2 方案作为备用,不作为主生产链路。批量脚本、音色目录、QA 标准均以 VoxCPM2 为准。
所有角色语音使用 **VoxCPM2**(本地部署,无需外部 API),通过声音克隆实现一致的角色音色。
@@ -569,6 +636,7 @@ python audio/batch_tts_voxcpm.py --source voice --dry-run
| `choiceSilent` | 下滑沉默 | 330Hz 极轻 |
| `typing` | 打字中 | 6 次随机频率短脉冲 |
| `send` | 发送 | 400→900Hz 上扫 |
| `longPress` | 长按确认 | 330→440Hz 缓升 |
| `ending` | 结局 | 三音渐弱 |
### 7.6 BGM 与环境音
@@ -592,9 +660,9 @@ python audio/batch_tts_voxcpm.py --source voice --dry-run
### 8.1 技术栈
纯前端 Web 应用(单 HTML + JS),无需后端服务器:
- **手势识别**`gestures.js`6 种手势(单击、双击、四方向滑动)
- **手势识别**`gestures.js`7 种手势(单击、双击、长按、四方向滑动)
- **音频管理**`audio.js` — Web Audio API 合成 + HTML5 Audio 播放
- **触觉反馈**`haptics.js` — Vibration API
- **触觉反馈**`haptics.js` — Vibration API(增强反馈,非必需;见下方降级说明)
- **剧情数据**`story.js` — 16 条消息的完整对话树
- **游戏引擎**`engine.js` — 状态机 + 异步对话流程
- **行为画像**`profile.js` — PROFILE 计分 + 状态变量
@@ -605,20 +673,43 @@ python audio/batch_tts_voxcpm.py --source voice --dry-run
idle → notification/ringing → playing → choosing → responding → reacting → [choosing | idle] → ending
```
| 状态 | 说明 | 可用手势 |
|------|------|---------|
| idle | 等待消息 | — |
| notification | 消息到达提示中 | 单击(播放)、双击(跳过) |
| ringing | 来电铃声中 | 单击(接听)、左滑(拒接) |
| playing | NPC 语音播放中 | 双击(跳过语音) |
| choosing | 等待玩家选择 | 右滑、左滑、上滑、下滑、单击 |
| responding | 林夏语音播放中 | 双击(跳过) |
| reacting | NPC 反应播放中 | 双击(跳过) |
| 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];
@@ -667,7 +758,29 @@ async _handleChoice(dir) {
- 中断当前语音播放
- 异步函数在每个步骤检查 skip 标志,快速跳到下一阶段
### 8.5 隐私承诺
### 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 隐私承诺
本作**不申请任何敏感权限**
- ❌ 不要麦克风、摄像头、位置、通讯录
@@ -707,7 +820,7 @@ async _handleChoice(dir) {
- 林夏 ~55 条,~100 秒
- NPC ~90 条,~835 秒
- **总语音约 145 条,约 15.5 分钟**
- UI 音效:12 种,全部 Web Audio 合成
- UI 音效:13 种,全部 Web Audio 合成
- BGM4 段
- 环境音:4-5 段
@@ -721,7 +834,7 @@ blind/
│ ├── index.html # 入口
│ └── js/
│ ├── main.js # 启动 + 事件绑定
│ ├── gestures.js # 6 种手势识别
│ ├── gestures.js # 7 种手势识别(含长按)
│ ├── haptics.js # 触觉反馈
│ ├── audio.js # 音频管理 + UI 合成音效
│ ├── story.js # 16 条消息剧情数据
@@ -763,7 +876,27 @@ blind/
| 手势误判导致选错 | 滑动阈值 30px + 40°范围;双击间隔 250ms 内 |
| 玩家不理解手势含义 | MSG-02(小美教学关)强引导,每步正向反馈 |
| 女性玩家代入感 | 编剧组需有 24 岁左右女性参与 |
| 题材敏感 | 开场加心理预警 + 提供试玩版 |
| 题材敏感 | 见下方"情绪安全设计"专节 |
### 情绪安全设计
本作涉及分手、裁员、独居、深夜来电、家庭创伤回忆等高情绪浓度内容,必须认真对待玩家的心理安全。
**开场内容提示**(游戏启动后、第一条消息前):
- 系统语音播报:"本游戏涉及情感压力、职场失落、家庭关系等主题。如果你当前情绪状态不佳,建议择日再玩。游戏过程中,两指长按可暂停,三指点击可直接退出。"
**游戏中安全机制**
- **随时暂停**:任何状态下两指同时长按 1 秒,游戏暂停,系统语音:"游戏已暂停。单击继续,三指点击退出。"(两指长按与单指双击完全不同,避免误触跳过)
- **安全退出**:三指同时点击,系统语音确认后直接跳转到结尾支持资源页。
- **长按状态播报**:让玩家随时确认自己在哪,减少焦虑感。
**结尾支持资源**(任何结局播放完毕后):
- 系统语音:"感谢你陪林夏度过这一夜。如果你现在需要和人说说话:全国 24 小时心理援助热线 400-161-9995;北京心理危机研究与干预中心 010-8295-1332。"
- 音频播放完毕后保持在屏幕上(即使是纯音频游戏,此处允许显示文字——用户此时可以看手机了,屏幕"修好了"是一个隐喻)。
**叙事安全原则**
- 林夏的自救不完全寄托在妈妈或男性角色身上——妈妈提供的是"被记住"的感觉,周南提供的是"被看见"的感觉,但最终让林夏起身烧水、给阿哲寄耳机的,是她自己的选择。
- MSG-16 妈妈语音中"那年夏天我没死"这句表述需谨慎处理:妈妈的语气是平静追忆而非戏剧化,TTS 合成时 emotion 设为 `gentle, peaceful`,避免制造突然的情绪冲击。
---