Files
36Wisdom/docs/superpowers/specs/2026-08-13-story-theater-design.md
T
adminandClaude Opus 4.7 4120e04b36 docs(spec): v2.2 验收标准 1 扩展——开场情境字幕与场景面板描述一并移除
用户确认「剧情不显示文字」范围含 SceneTheater 情境字幕与 scene-card
情境描述,仅保留场景名/emoji/装饰;开场与情境内容仍自动朗读

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-08-14 09:14:29 +08:00

16 KiB
Raw Blame History

故事剧场化设计(表现形式升级:剧场 + 全互动决策)

日期:2026-08-13 | 状态:已获用户批准(含 v2 修订)| 关联:M1.5 互动体验(docs/superpowers/plans/2026-08-13-m1_5-interactions.md 之后的新增设计)

背景与问题

当前每计谋关卡的表现形式为「文字情境 + 文字选项列表 + 点评弹层」。走查反馈:呈现形式生硬、考试感强,无法引起 4-8 岁儿童兴趣。核心矛盾:决策树数据模型(情境 → 决策 → 分支 → 结果 → 点评 → 完美机制)是教育价值的载体,必须完整保留;需要改变的是呈现层——把「读题、答题」变成「看小剧场、动手玩」。

v2 修订(2026-08-13):互动剧情演绎

用户走查反馈(v1 落地后):关卡仍是「读一段描述 → 答题」,决策没有融入剧情流,情境描述简略,孩子没兴趣。v1 解决「怎么演」(三幕演出壳),v2 解决「演什么」——内容剧本化:每个决策节点从一段描述文字变成一场微型剧情演绎(台词流),决策与反馈嵌入剧情流。决策树模型仍零改动:v2 只新增 scene_node.script 列 + 前端 StoryPlayer + 剧本生成管线。

剧本内容来源(方案 3):oMLX 批量生成草稿 + 人工精修 + 导入回填。

核心转变

维度 v1(现状) v2(剧情演绎)
内容 node.content 一段描述文字 script 3-6 句台词流:旁白 + 角色对话 + 末句角色提问
呈现 静态节点卡片 + 朗读按钮 StoryPlayer 逐句演绎:对话气泡 + 自动朗读 + 逐字高亮 + 表情切换
决策 卡片下方「答题」 剧情流内角色提问 → ActionCard 直接出现在剧情流
反馈 弹窗点评(你的选择 + ✓/✗) 角色回应气泡融入剧情流(pros/cons 转角色台词)
内容生产 种子数据手工撰写 oMLX 生成草稿 → 人工精修 → 导入回填

数据模型(最小改动)

scene_node 新增一列(dao 迁移 + EnsureColumn,模式同 *_pinyin 列):

  • script TEXT:JSON 数组台词流,每节点 3-6 句:
[
  {"speaker": "旁白", "text": "夜深了,城里静悄悄的。", "emotion": "normal"},
  {"speaker": "小军师", "text": "城门关着,怎么进去呢?", "emotion": "think"}
]
  • speaker旁白 或角色名(node.character.name),旁白居中无头像,角色带头像气泡
  • text:一句台词 8-30 字(含标点)
  • emotionnormal | happy | sad | think | surprise → mood 徽章 🤔 😊 😢 🧐 😮
  • 结构约束(生成校验器强制):决策节点(interaction_type=1)末句必须为角色提问(speaker=角色名、结尾「?」);终局节点(result_type>0)末句为角色总结(欣慰/鼓励);其他句 speaker 自由(旁白/角色交替)
  • pinyin 不入草稿:导入时用 common.AnnotatePinyin 对每句 text 生成逐字注音 JSON(人工精修会改文本,导入时统一重算),前端 RubyText 既有机制逐字对齐

StoryPlayer 组件(新)

输入 script + character + scene + showPinyin,逐句演绎:

  1. 对话气泡入场(CSS 淡入上滑):旁白居中(无头像、斜体/描边区分),角色左侧头像 + 名字 + 气泡
  2. 自动朗读该句(speech.speak 逐句,TTS 降级)
  3. 逐字高亮:RubyText 扩展 highlight prop(当前朗读字下标),点亮进度跟随朗读
  4. 句间停顿(≤300ms/字),emotion 切换 mood 徽章弹跳(复用 .char-mood 动画)
  5. 播完 @done;右上「跳过」立即完成

script 为空:StoryPlayer 不渲染,回退现有卡片呈现(零变化)。

play.vue 剧情流

  • 节点区从「独立卡片」改为「剧情流 scroll-view」:当前节点剧本演绎 → 播完 ActionCard 出现在剧情流底部(复用现有 ActionCard/触控组件)→ 选择提交 → 角色回应气泡融入剧情流(用 feedback_pros/cons 作为角色台词 + 分支演出 mood/音效,替代弹窗)→ 「继续」→ 下一节点剧本继续演绎,剧情流滚动增长
  • 场景横幅(scene-card 精简为顶部横幅)固定,镜头感;进度点保留
  • level/choose 接口、判分、完美机制、路由记录零改动;反馈 pros/cons 文本由 genasset 点评管线生成(已有)

剧本生成管线(并入 genasset

  • --only=scripts:对 scene_node 逐节点调 oMLXprompt:情境 content + 选项 + 角色名 → 台词流,风格约束:儿童化口语、每句 8-30 字、3-6 句、末句提问/总结)→ 校验(结构约束 + JSON 解析 + 长度)→ 草稿写 workspace/scripts/{level_id}/{node_id}.json(含待修标记,人工可编辑)
  • --import-scripts:草稿 → 再校验 → 每句 AnnotatePinyinUPDATE scene_node.script,幂等(已有 script 跳过)
  • 与素材/点评管线同池并发 2、同 --strategy/--force 控制

前端回退与渐进升级

  • script 为空 → 现有 v1 呈现零变化;36 关逐关生成回填,前端自动升级为剧情演绎
  • 不阻塞 v1:阶段 A 落地即可验收;B/C 逐关生效

验收标准(v2

  1. 决策节点演绎台词流:气泡 + 自动朗读 + 逐字高亮 + 表情切换,末句角色提问
  2. ActionCard 出现在剧情流内(非独立答题区),触控 5 种互动同样嵌入
  3. 选择后角色回应气泡融入剧情流,无弹窗;分支演出(mood/音效)保留
  4. script 为空节点完整回退现有呈现
  5. 管线:--only=scripts 草稿 → 人工精修 → --import-scripts → 前端生效;校验拒绝不合规草稿
  6. level/choose 判分与完美机制行为不变(单测全过)

实施拆分

  • 阶段 A(数据 + 后端 + StoryPlayer + 剧情流)scene_node.script 列迁移 + NodeVO.Script 透出 + RubyText highlight prop + StoryPlayer 组件 + play.vue 剧情流集成(回退保护)
  • 阶段 B(生成管线)genasset --only=scripts / --import-scripts + 第 1 计试点生成并精修 1 关
  • 阶段 C(全量):36 关剧本后台生成 + 人工精修流程 + 走查验收

v2.1 修订(2026-08-13):全自动演出 + 文字精简

用户走查反馈(v2 落地后):「现在的情景执行依赖于点击按钮,这是不对的,应该自动的,而且也不需要显示这么多文字,文字多了,孩子更不喜欢」。v2 解决「演什么」,v2.1 解决「怎么演得像动画」——消除按钮依赖、削减屏幕文字量。数据模型零改动;接口仅 FinalSettle 多返回 final_node(复用既有 NodeVO 链路,含 script)。

核心转变

维度 v2(现状) v2.1(自动演出)
开场 SceneTheater 朗读后需点「开始 ›」 朗读完成后自动进入决策幕(保留「跳过 ›」)
反馈 回应气泡需点「继续 ›」 气泡出现约 3 秒后自动继续(点击可立即继续,计时器防重复)
终局 choose 到终局直接跳结算页,结局台词从未播放 先由 StoryPlayer 演绎终局节点台词(final_node.script),播完自动跳结算
文字量 StoryPlayer 3-6 行完整堆叠 已播行折叠:只保留当前行 + 上一行淡出

数据与接口(最小改动)

  • FinalSettle 增加 final_nodeNodeVO,含 script):service Choose 终局分支把终局节点记录(既有 nextRec)经 nodeOf 构建为 Node 一并返回;controller 映射到 dto
  • play.vue data.final 分支:先存 RESULT_KEY(同现状),若 final_node.script 非空 → 进入终局演绎态(StoryPlayer 播 final_node 剧本)→ @doneredirectTo 结算页;script 为空 → 直接跳结算(行为同现状)

自动节奏

  • SceneTheater:移除「开始 ›」按钮;朗读结束(mounted 的 setTimeout,同现状)自动 emit done;「跳过 ›」保留
  • 回应气泡(剧情流 reply):出现后 3 秒自动 continuePlay();点击「继续 ›」立即继续;自动计时触发前点击需 clearTimeout 防重复推进;v1 弹窗反馈(feedback)不加自动继续(保持人工节奏)
  • 终局演绎:StoryPlayer 播完自动跳结算(3-6 句 × 8-30 字,约 10-25 秒);跳过按钮同样可跳过终局台词直接跳结算

前端回退与渐进升级

  • final_node.script 为空 → 直接跳结算(现状不变)
  • 终局演绎复用 StoryPlayer 与剧情流形态(:key 强制重建、done → 跳转),不新增组件

验收标准(v2.1

  1. 开场自动进入决策幕(无「开始」按钮),「跳过」仍可用
  2. 回应气泡约 3 秒自动继续;点击可立即继续;无重复推进(连点/自动+点击不双跳)
  3. 终局节点台词先演绎再自动跳结算;final_node.script 为空直接跳结算(回退)
  4. StoryPlayer 已播行折叠:当前行 + 上一行淡出,无 3-6 行完整堆叠
  5. 判分与完美机制行为不变(level_play 单测全过)

v2.2 修订(2026-08-13):纯语音演绎 + 单端口托管

用户走查反馈(v2.1 落地后):① 「必须点跳过按钮才显示出选项」——StoryPlayer 自然播完不触发 donespeak onEnd 在部分浏览器环境不回调,逐句推进只能靠 20s forceNext 兜底,体验像卡死);② 「剧情也不应该显示文字,直接语音播放就可以了不需要文字展示区」;③ 「前端应该跟后端共用端口」(后端 :8080,前端产物由后端托管)。

核心转变

维度 v2.1 v2.2
剧情呈现 气泡文字 + 逐字高亮 + 拼音 纯语音演绎:无文字展示区,角色立绘 + mood 表情 + 逐句朗读
句推进 speak onEnd + 逐字高亮双门控 speak onEnd 驱动 + 文本长度估时兜底320ms/字 + 1.2s,最短 2.5s);20s forceNext 保留为最后防线
部署 H5 dev server 5173(开发代理) 前端产物 ui-src/dist 由后端 :8080 AddStaticPath 托管(生产单端口)

验收标准(v2.2

  1. 剧情无任何文字展示:只有角色 + 表情徽章 + 语音(用户确认后扩展到开场:SceneTheater 情境字幕(含拼音逐字、「正在朗读…」状态)与场景面板情境描述scene-card 的 RubyText + 「读一读情境」按钮)一并移除,仅保留场景名/emoji/装饰;开场与情境内容仍自动朗读)
  2. 台词播完后选项自动出现(无需点跳过);跳过按钮仅作可选的手动加速
  3. speak onEnd 不回调的环境(模拟浏览器静音策略/无语音)下按估时兜底自动推进,不卡死
  4. http://localhost:8080 直接打开应用(前后端同端口)

设计总览

每关 = 一部 2-3 分钟儿童小剧场,三幕结构:

第 1 幕 开场演出  →  第 2 幕 决策演出(循环 2-6 次)  →  第 3 幕 结局演出
  旁白+字幕高亮       动作卡 / 触控互动 / 分支演出           庆祝 / 鼓励
  角色道具入场        决策后弹点评气泡
  • 剧场化是容器(怎么演给孩子看):开场/分支/结局的动画演出
  • 全互动是交互方式(孩子怎么动手):每个决策点按内容映射为动作卡或触控互动,消灭纯文字选项列表
  • 决策树模型零改动scene_node / node_option 表结构、level/choose 接口、判分与完美机制全部不动

第 1 幕:开场演出

替代现在静止的场景卡片(play.vue 的 .scene-card)。

元素 实现 现状
场景横幅 场景插画(genasset 素材)或 emoji 兜底 已有
角色入场 角色立绘从画面外滑入/弹跳落地(CSS keyframes 素材已有管线
道具飞入 情境相关道具图标依次飞入(CSS 动画 + 延迟) 素材已有管线
旁白朗读 自动调用 SpeakButton 的朗读(TTS 降级),场景 audio 优先 已有
字幕高亮 RubyText 逐字显示(拼音注音同步) 已有
播完状态 角色 mood 切「🤔」,显示「开始」按钮进入决策幕
  • 提供「跳过」按钮(右上角),跳过直接进入决策幕
  • 时间预算:8-15 秒,可跳过

第 2 幕:决策演出

互动形态自动判定(前端运行时,后端零改动)

对每个决策节点,按以下顺序判定互动形态:

1. 节点 config 非空(入口互动节点,interaction_type ∈ {2,7,5,3,8})→ 用现有互动组件
2. 节点 interaction_type === 1(普通决策节点)→ 按选项内容特征自动选形态:
   a. 全部选项都带 prop 且 prop 去重后数量 = 选项数 → 道具选择 PropPick
   b. 选项文本含步骤序词("先/再/最后")或选项含"排序"特征 → 步骤排序 StepSort
   c. 选项文本含"找到/发现/寻找" → 找线索 FindSpot
   d. 选项文本含"配对/搭配/谁+什么" → 连线配对 LinkMatch
   e. 其余 → 动作卡(兜底)

判定规则 a-e 为启发式,误判时教育语义不受影响:互动成功/失败只决定走向 success/fail 出口(现有机制),动作卡兜底保证任何节点都有可玩呈现。规则实现在 play.vue 的单一函数 pickInteraction(node) 中,返回组件名,渲染 v-if 分发保持不变。

动作卡组件(新,替代文字选项列表)

interaction_type === 1 且不满足 a-e 的节点,渲染 ActionCard 组件:

  • 每个选项一张卡:道具图标/表情(prop 素材或 emoji 兜底)+ 短语(RubyText 拼音注音)+ 序号徽章
  • 按压反馈:点击时卡片下沉+弹跳(CSS active 变换 + keyframes
  • 每张卡可「听一听」(SpeakButton 复用,读选项文字)
  • 卡片底色按选项序号取 c1-c4 四色边框(现有样式类)
  • 点击 → 播放「选择演出」后提交 choose(见下)

分支演出动画(新)

每个决策提交后、弹点评前,播放 0.8-1.5 秒轻量过渡(不阻断节奏):

  • 角色表情切换(mood 徽章 😊/😢/😮 弹跳,现有 .char-mood 动画扩展)
  • 场景元素变化(道具图标飞入/消失,CSS keyframes
  • 配套音效(playSound good/bad 已有)
  • 演出结束后弹点评气泡(现有三段式:你的选择 + ✓好处 + ✗对比)

节奏控制

  • 开场可跳过;分支演出 ≤1.5 秒;单关时长保持 5-10 分钟
  • 点评弹层已有「继续」按钮,流程不变

第 3 幕:结局演出

闯关内结算(result 页升级)

  • 成功(通关/完美):角色跳舞 + 彩带/烟花粒子(CSS 动画)+ 星星雨 + finish 音效
  • 失败:角色安慰表情 + 鼓励语("再试一次,你已经很棒了")+ 无惩罚感

配套

  • 结算页的彩带/星星雨动画 = Task 11 已规划的 confetti,合并实现
  • 路线回顾卡片保留(已规划)

配套改动

内容 阶段
素材规格扩展 genasset prompt 模板增加角色表情帧(高兴/难过/困惑/正常)与动作帧(入场/跳舞/安慰);素材未生成时 emoji 兜底 C
互动覆盖全 36 关 种子数据为 22 个无道具选项的关卡补互动入口所需道具数据(或后台运营补),使入口互动覆盖全关卡 B
结算页升级 confetti + 角色表情 + 鼓励语 A
文档 README「情境闯关」行、技术设计.md 4.13 补充剧场化设计 A

实施拆分(3 个阶段,各自可独立验收)

  • 阶段 A(纯前端,立即见效):开场演出(scene 面板升级 + 跳过)+ 动作卡组件(ActionCard)+ 分支演出动画 + 结局演出(result 页)
  • 阶段 B(覆盖补齐):前端 pickInteraction 自动判定(5 种触控互动按内容特征命中)+ 种子数据补 22 关互动入口
  • 阶段 C(素材增强):genasset 表情/动作素材规格扩展(角色 3 态表情帧 + 动作帧)

阶段 A 独立成里程碑(M1.6 或并入 M1.5 收尾),B/C 视素材管线(Task 10)完成度顺延。

验收标准

  1. 关卡开场自动演出(旁白 + 字幕高亮 + 角色/道具入场),可跳过
  2. 决策点无纯文字选项列表:全部渲染为动作卡或触控互动
  3. 每个决策提交后有分支演出动画 + 点评弹层
  4. 结算页成功/失败有动画演出
  5. 现有接口/判分/完美机制行为不变(level_play 单测全过)
  6. 36 关均有关卡入口互动(阶段 B 完成时)