Files
tianshu-engine/ROADMAP.md

548 lines
24 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.

# 交互式电影游戏引擎 — Roadmap
## 技术栈
- **框架**: Vue 3 (Composition API + `<script setup>`)
- **构建**: Vite
- **状态管理**: Pinia
- **可视化编辑器**: Vue Flow
- **存储**: IndexedDB (Dexie.js)
- **语言**: TypeScript
- **视频**: 原生 `<video>` + A/B 双缓冲
## 项目结构
```
moviegame/
├── engine/ # 框架无关的核心引擎(纯 TS
│ ├── core/
│ │ ├── Engine.ts # 主循环,驱动各子系统
│ │ ├── SceneManager.ts # 剧情节点图遍历
│ │ ├── VideoManager.ts # A/B 双缓冲视频播放
│ │ └── StateManager.ts # 全局状态、条件求值
│ ├── systems/
│ │ ├── ChoiceSystem.ts # 选择 UI + 倒计时
│ │ ├── QTESystem.ts # QTE 触发、判定、超时
│ │ └── SaveSystem.ts # IndexedDB 存取
│ └── types.ts # 场景数据类型定义
├── src/ # Vue 应用(播放器端)
│ ├── components/
│ │ ├── GamePlayer.vue # 主播放器(挂载 video 元素)
│ │ ├── ChoicePanel.vue # 选项面板
│ │ ├── QTEOverlay.vue # QTE 遮罩
│ │ ├── SaveLoadMenu.vue # 存档界面
│ │ └── Subtitles.vue # 字幕显示
│ ├── composables/
│ │ ├── useGameEngine.ts # 引擎实例 + 生命周期
│ │ ├── useVideoPlayer.ts # 视频状态响应式封装
│ │ └── useGameState.ts # 状态响应式封装
│ ├── stores/
│ │ └── gameStore.ts # 游戏全局状态Pinia
│ ├── App.vue
│ └── main.ts
├── editor/ # Vue Flow 可视化编辑器(独立入口)
│ ├── components/
│ │ ├── SceneGraph.vue
│ │ ├── NodeEditor.vue
│ │ └── PreviewPanel.vue
│ ├── composables/
│ │ └── useGraphEditor.ts
│ └── App.vue
├── public/
│ ├── videos/ # 视频资源
│ └── scenes/
│ └── demo.json # 示例剧情数据
├── vite.config.ts
├── tsconfig.json
├── package.json
└── index.html
```
## 场景数据格式
```typescript
// engine/types.ts
interface SceneNode {
id: string;
videoUrl: string;
subtitleUrl?: string;
choices?: Choice[];
qte?: QTEDefinition;
nextScene?: string;
onEnter?: Effect[];
}
interface Choice {
text: string;
targetScene: string;
conditions?: Condition[];
effects?: Effect[];
timeLimit?: number; // 限时选择0=不限时
}
interface Condition {
variable: string;
op: '>' | '<' | '>=' | '<=' | '==' | '!=' | 'hasFlag';
value: number | string | boolean;
}
interface Effect {
type: 'set' | 'add' | 'toggleFlag' | 'triggerEvent';
target: string;
value?: number | string | boolean;
}
interface QTEDefinition {
triggerTime: number;
prompt: string;
keys: string[];
timeLimit: number;
successScene: string;
failScene: string;
effects?: {
success: Effect[];
fail: Effect[];
};
}
interface GameData {
scenes: Record<string, SceneNode>;
startScene: string;
variables: Record<string, number>;
}
interface SaveData {
slot: number;
timestamp: number;
currentScene: string;
variables: Record<string, number>;
flags: string[];
history: ChoiceRecord[];
thumbnail?: string;
}
```
## 实现路线
### P0 MVP — 最小可玩原型3-5 天)✅ 已完成 2026-06-07
目标:能播放一段视频 → 弹出选项 → 跳到下一段视频
- [x] 项目脚手架Vite + Vue3 + TypeScript + Pinia
- [x] `engine/core/Engine.ts` — 主循环骨架(加载场景 → 播放 → 等选择 → 切换)
- [x] `engine/core/SceneManager.ts` — 加载 JSON按 ID 查找场景节点
- [x] `engine/core/VideoManager.ts` — 单 video 元素播放,监听 ended 事件
- [x] `engine/core/StateManager.ts` — 变量存取、条件求值、效果执行
- [x] `engine/types.ts` — 类型定义
- [x] `src/components/GamePlayer.vue` — 挂载 video控制播放
- [x] `src/components/ChoicePanel.vue` — 渲染选择按钮,触发引擎切换
- [x] `public/scenes/demo.json` — 编写一段简单剧情7 个场景节点)
- [x] 验证:从 demo.json 加载场景,能走通 开始→选择→分支播放→结束 流程
### P1 核心 — 无缝切换 + 条件分支 + 存档1-2 周)✅ 已完成 2026-06-07
- [x] `engine/core/VideoManager.ts` 升级 — A/B 双缓冲预加载候选视频CSS 交叉淡化
- [x] `engine/core/SceneManager.ts` 升级 — 支持条件分支(根据 variables/flags 过滤选项)
- [x] `engine/systems/SaveSystem.ts` — Dexie.js IndexedDB 存取,多槽位
- [x] `engine/systems/ChoiceSystem.ts` — 限时选择倒计时,超时默认选择
- [x] `src/components/SaveLoadMenu.vue` — 存档/读档 UI
- [x] `src/stores/gameStore.ts` — Pinia 全局状态管理(含计时器、存档列表)
- [x] `src/composables/useGameEngine.ts` — 桥接层(双 video、存档、计时器
- [x] `src/components/GamePlayer.vue` — 双 video 元素 + 交叉淡化 CSS
- [x] `src/components/ChoicePanel.vue` — 倒计时进度条 + 计时文字
- [x] `src/App.vue` — 整合 SaveLoadMenu、双 video、计时器
- [x] 验证:条件分支走通,存档读档正常,视频切换交叉淡化
### P2 进阶 — QTE + 字幕 + 多存档槽1 周)✅ 已完成 2026-06-07
- [x] `engine/systems/QTESystem.ts` — QTE 触发、键盘监听(支持多键匹配)、超时判定
- [x] `src/components/QTEOverlay.vue` — SVG 倒计时环 + 按键提示 + 成功/失败动画
- [x] `src/components/Subtitles.vue` — WebVTT 解析 + 字幕同步渲染
- [x] `engine/core/Engine.ts` — 集成 QTEtimeupdate 检测 + 条件跳转 + 效果应用)
- [x] 多存档槽位 + 存档缩略图canvas 截图当前视频帧320x180 JPEG
- [x] `engine/core/VideoManager.ts` — 新增 `getActiveVideoElement()` 供截图
- [x] `engine/systems/SaveSystem.ts` — DB 版本升级 v2支持 thumbnail 字段)
- [x] `src/components/SaveLoadMenu.vue` — 存档缩略图预览
- [x] 完整事件总线sceneChange, choiceRequest, choiceTimer, choiceTimeout, videoEnd, qteTrigger, qteTimer, qteResult, gameEnd
- [x] 验证QTE 正常触发与判定ArrowLeft/ArrowRight/A/D 躲石块),字幕同步,存档缩略图正常
### P3 编辑器 — 可视化剧情编辑2-3 周)✅ 已完成 2026-06-07
- [x] 编辑器入口:独立 `editor/index.html` + `editor/main.ts`Vite 多入口构建)
- [x] `editor/components/SceneGraph.vue` — Vue Flow 节点图(场景节点 + 分支/默认/QTE 连线)
- [x] `editor/components/NodeEditor.vue` — 右侧面板(视频/字幕路径、nextScene、选项增删改、QTE 参数编辑)
- [x] `editor/components/PreviewPanel.vue` — 嵌入播放器实时预览选中场景视频
- [x] `editor/composables/useGraphEditor.ts` — 图数据与 JSON 双向同步
- [x] JSON 导出/导入(文件下载 + 文件选择)
- [x] 工具栏:新建场景、导入 JSON、导出 JSON、加载示例、起始场景选择
- [x] `vite.config.ts` — 多页面构建main + editor
- [x] 验证:编辑器能产出合法 JSON引擎能正确加载并运行
### P4 图片热点 — 点击图片触发分支(待实现)
目标:场景支持静态图片替代视频,图上定义可点击热区,点击不同位置触发不同分支
**场景数据设计:**
```json
{
"id": "crime_scene",
"type": "image",
"imageUrl": "/images/crime_scene.jpg",
"subtitleUrl": "/subtitles/crime_scene.vtt",
"hotspots": [
{
"id": "hs_desk",
"x": 0.15, "y": 0.25, "width": 0.18, "height": 0.12,
"label": "查看书桌",
"targetScene": "desk_detail",
"conditions": [{ "variable": "investigation", "op": ">=", "value": 1 }],
"effects": [{ "type": "setFlag", "target": "checked_desk" }]
},
{
"id": "hs_window",
"x": 0.72, "y": 0.08, "width": 0.15, "height": 0.28,
"label": "查看窗户",
"targetScene": "window_detail"
},
{
"id": "hs_body",
"x": 0.40, "y": 0.40, "width": 0.10, "height": 0.22,
"label": "检查尸体",
"targetScene": "body_detail",
"timeLimit": 10
}
],
"choices": [
{ "text": "离开现场", "targetScene": "leave_room" }
]
}
```
**字段约定:**
- `type: "image"` — 声明为图片场景(默认/不存在则为视频场景)
- `x/y/width/height` — 热区坐标,使用**相对比例**0~1自适应屏幕尺寸
- 图片场景仍可同时附带底部 `choices`(如"离开"选项)
- `hotspots` 支持 `conditions`(条件显隐)、`effects`(点击后效果)、`timeLimit`(限时)
**实现清单:**
- [ ] `engine/types.ts``SceneNode.type` 字段、`Hotspot` 接口
- [ ] `engine/core/Engine.ts` — 支持 `type: "image"` 场景,挂载图片 + 热区
- [ ] `src/components/ImageScene.vue` — 渲染图片 + 热区矩形 + hover 高亮 + label 浮动提示
- [ ] `editor/components/NodeEditor.vue` — 场景类型切换(视频/图片)+ 热区可视化编辑(拖放矩形)
- [ ] `public/scenes/demo.json` — 新增图片场景示例 + 示例图片
- [ ] 验证:图片加载、热区点击触发分支、条件过滤、限时热区
### P5 选择等待循环 — 视频结束循环播放(待实现)
目标视频结束后不暂停画面而是无缝切换到一段循环视频Idle Loop选项浮在循环画面之上
消除"画面静止等选择"的割裂感提升电影感。这是《底特律变人》《Late Shift》等商业游戏的标配做法。
**场景数据设计:**
```json
{
"id": "tense_moment",
"videoUrl": "/videos/tense.mp4",
"loopVideoUrl": "/videos/tense_loop.mp4",
"choices": [
{ "text": "冒险救人", "targetScene": "rescue" },
{ "text": "悄悄离开", "targetScene": "flee" }
]
}
```
**工作流程:**
```
主视频 A槽播放 ──→ ended ──→ B槽切换 loopVideo (loop=true) + 淡入 ──→ 显示选项
用户点击选择 ────┘
停止循环 → 切换到目标场景
```
**实现清单(基于现有 A/B 双缓冲架构,改动量小):**
- [ ] `engine/types.ts``SceneNode.loopVideoUrl?: string`
- [ ] `engine/core/VideoManager.ts``playLoop(url)` 新方法(复用 inactive 槽,`loop=true` + 交叉淡化)
- [ ] `engine/core/Engine.ts` — 视频 ended 时检测 `loopVideoUrl`,有则调用 `playLoop` 而非直接触发选项;用户选择后停止循环
- [ ] `src/components/ChoicePanel.vue` — 无改动(选项已浮在视频层之上)
- [ ] `public/scenes/demo.json` — 示例场景添加循环视频
- [ ] 验证:主视频结束 → 无缝循环 → 选项中循环播放 → 选择后停止 → 下一场景
### P6 独立背景音乐 — 画面循环不打断 BGM待实现
目标:将背景音乐从视频中剥离,由独立 AudioManager 驱动。视频循环/切换时 BGM 保持连贯播放,
不同场景之间用交叉淡化衔接。这也是《底特律变人》《The Dark Pictures Anthology》等商业游戏的标配。
**架构变更:**
```
Engine
├── VideoManagerA/B 双缓冲,只管画面和视频内音轨)
│ └── loopVideo 循环时只管画面 → BGM 不受影响
└── AudioManager独立 AudioContext/HTMLAudioElement只管 BGM
└── 按场景播放/交叉淡化/循环,独立于视频生命周期
```
**场景数据设计:**
```json
{
"id": "tense_moment",
"videoUrl": "/videos/tense_no_bgm.mp4",
"loopVideoUrl": "/videos/tense_loop_no_bgm.mp4",
"bgmUrl": "/audio/tense_bgm.mp3",
"bgmVolume": 0.8,
"bgmCrossFade": 2.0,
"bgmContinue": true,
"videoMuted": true,
"choices": [...]
}
```
**工作流对比:**
| 阶段 | 改进前 | 改进后 |
|------|--------|--------|
| 主视频播放 | 视频自带 BGM | AudioManager 独立播 BGM视频 muted |
| 进入选择等待 | 循环视频 → BGM 断掉重来 | 视频循环 → BGM 继续连贯 |
| 选择后切场景 | 下一段视频 BGM 硬切 | BGM 交叉淡化过渡bgmCrossFade 秒) |
| 关音乐/调音量 | 无法控制 | AudioManager 提供音量/静音接口 |
| 同一 BGM 多场景 | 每个 mp4 嵌一份,浪费 | 共用同一文件,节省带宽 |
**实现清单:**
- [ ] `engine/systems/AudioSystem.ts` — 新增BGM 播放、交叉淡化GainNode ramp、循环、音量/静音
- [ ] `engine/core/Engine.ts` — 集成 AudioSystem场景切换时对比 bgmUrl同源→继续不同→crossFade
- [ ] `engine/types.ts``SceneNode``bgmUrl``bgmVolume``bgmCrossFade``bgmContinue``videoMuted`
- [ ] `engine/core/VideoManager.ts` — 支持 `muted` 参数(视频音轨静音但在 BGM 模式下禁用)
- [ ] `public/scenes/demo.json` — 示例场景拆分视频+音频
- [ ] `editor/components/NodeEditor.vue` — BGM 字段编辑面板
- [ ] 验证BGM 跨越视频循环连续播放、场景切换交叉淡化、音量控制生效
### P7 全屏模式 — 沉浸式浏览器体验(待实现)
目标:一键进入全屏播放模式,播放中自动隐藏 UI选项/菜单等浮层除外),提供 F11 级别的沉浸感。
**实现清单:**
- [ ] `src/composables/useFullscreen.ts` — Fullscreen API 封装(`element.requestFullscreen()`ESC 退出监听)
- [ ] `src/App.vue` — 全屏按钮(工具栏或浮动按钮);播放中隐藏非关键 UI 元素
- [ ] 指针空闲隐藏:鼠标 3 秒不动自动隐藏光标(`pointer-events: none` 过渡)
- [ ] 全屏下选项面板仍可见z-index 高于视频层)
- [ ] 验证F11 等效全屏、ESC 退出、播放中 UI 自动隐藏/鼠标移动恢复
### P8 章节选择 — 通关后可跳转(待实现)
目标:玩家通关后,可从中途任意章节起始点重新开始。每个章节记录一个入口场景 ID展示章节缩略图和标题。
**数据结构设计:**
```json
{
"chapters": [
{ "id": "ch1", "label": "第一章:醒来", "startScene": "intro", "thumbnail": "/images/ch1.jpg" },
{ "id": "ch2", "label": "第二章:抉择", "startScene": "left_door", "thumbnail": "/images/ch2.jpg" }
]
}
```
**实现清单:**
- [ ] `engine/types.ts``GameData.chapters` 字段、`ChapterInfo` 接口
- [ ] `engine/systems/SaveSystem.ts` — 章节解锁状态持久化(已通关章节记录)
- [ ] `src/components/ChapterSelect.vue` — 章节选择界面:缩略图网格 + 标题 + 锁定/解锁状态
- [ ] `engine/core/Engine.ts``startChapter(chapterId)` 方法,跳转到指定章节起始场景
- [ ] `src/App.vue` — 主菜单:新游戏 / 章节选择 / 继续
- [ ] 验证:通关后章节解锁、从章节入口跳转正确、未解锁章节灰显
### P9 跳过已看 + 倍速播放(待实现)
目标玩家重玩时可以跳过已看过的场景或加速播放2x/4x避免重复等待。
**实现清单:**
- [ ] `engine/systems/SkipSystem.ts` — 记录已观看的场景 ID 列表(持久化到 IndexedDB
- [ ] `src/components/SkipIndicator.vue` — "按住 Space 跳过" 进度环指示器
- [ ] `engine/core/VideoManager.ts``setPlaybackRate(rate)` 方法(原生 `video.playbackRate`
- [ ] `src/App.vue` — 跳过按钮/快捷键Space 长按 → 进度环走满 → 触发 skip倍速切换按钮1x/2x/4x
- [ ] `engine/core/Engine.ts` — 跳过逻辑:跳过 → 直接触发 `onVideoEnd` 流程;倍速不触发跳过
- [ ] `public/scenes/demo.json` — 添加 `skippable: true/false` 字段(关键场景禁止跳过)
- [ ] 验证:已看场景可跳过、未看不可跳过、倍速切换正常、关键场景不可跳过
### P10 键盘/手柄导航(待实现)
目标支持纯键盘或手柄操作整个游戏流程选择选项、确认、QTE、菜单适配"躺沙发"体验。
**实现清单:**
- [ ] `engine/systems/InputSystem.ts` — 统一输入抽象层:键盘(方向键/WASD+ 手柄Gamepad API+ 鼠标
- [ ] 选项高亮导航:↑↓ 移动焦点Enter/Space 确认,有视觉高亮指示器
- [ ] QTE 键位整合到 InputSystem目前 QTE 直接监听 `keydown`
- [ ] `src/components/ChoicePanel.vue` — 键盘焦点环样式(`focus-visible`
- [ ] `src/App.vue` — 菜单键Esc 打开/关闭菜单)
- [ ] 验证纯键盘完成一次完整流程开始→选择→QTE→存档→读档→结束、手柄连接时自动切换
### P11 多语言字幕(待实现)
目标支持多语言字幕切换UI 文本国际化,同一个场景可有中/英/日等多个字幕文件。
**数据结构设计:**
```json
{
"id": "intro",
"videoUrl": "/videos/intro.mp4",
"subtitles": {
"zh": "/subtitles/intro_zh.vtt",
"en": "/subtitles/intro_en.vtt",
"ja": "/subtitles/intro_ja.vtt"
},
"choices": [...]
}
```
**实现清单:**
- [ ] `engine/types.ts``SceneNode.subtitles` 改为 `Record<lang, url>``Choice.text` 支持多语言 key
- [ ] `engine/systems/I18nSystem.ts` — 语言切换、当前语言持久化到 localStorage
- [ ] `src/components/Subtitles.vue` — 监听语言切换,动态加载对应 VTT
- [ ] `src/components/ChoicePanel.vue` — 选项文字支持 i18n 映射
- [ ] `src/components/LanguageSwitch.vue` — 语言选择下拉菜单(顶部或菜单中)
- [ ] `public/scenes/demo.json` — 中英双语字幕示例
- [ ] 验证:语言切换后字幕/UI 即时更新、刷新保持语言偏好
### P12 场景过渡特效(待实现)
目标:场景切换不再只有 opacity 交叉淡化支持更多电影转场语言淡黑fade to black、白闪fade to white、模糊blur、滑动等。
**实现清单:**
- [ ] `src/components/TransitionOverlay.vue` — 全屏遮罩层CSS transition 控制颜色和透明度
- [ ] `engine/core/Engine.ts``SceneNode.transition?: TransitionDef`,在执行视频切换前先播放过渡
- [ ] `engine/types.ts``TransitionDef { type: 'fadeBlack' | 'fadeWhite' | 'dissolve' | 'cut' | 'blur'; duration: number }`
- [ ] `engine/core/VideoManager.ts` — 过渡时序:过渡遮罩覆盖 → 切换视频 → 过渡遮罩消失
- [ ] 验证:淡黑转场、白闪转场、不同 duration 参数生效
### P13 重玩驱动系统 — 成就 + 统计 + 结局画廊 + 关键选择提示(待实现)
目标:增加重玩驱动力。告知玩家"还有未发现的内容",激发探索欲望。涵盖 Telltale 的"[某人]会记住"、
Quantic Dream 的结局流程图、Steam 式成就系统、通关后的全局统计。
**子功能清单:**
**13a. 关键选择提示:**
- [ ] `Choice.prompt?: string` — 选择前弹出的提示文字(如 "[某人] 会记住你的选择"
- [ ] `src/components/ChoicePanel.vue` — 选项确认前展示提示动画(短暂放大/光效)
**13b. 成就系统:**
- [ ] `engine/systems/AchievementSystem.ts` — 成就定义:`{ id, title, description, icon, condition(scene, state) }`
- [ ] 成就触发检测(场景切换时匹配条件),解锁持久化到 IndexedDB
- [ ] `src/components/AchievementToast.vue` — 解锁时弹出提示(底部 toast + 音效)
- [ ] `src/components/AchievementPanel.vue` — 成就列表页面(全部/已解锁/未解锁)
**13c. 结局画廊:**
- [ ] `GameData.endings` — 结局定义:`{ id, label, thumbnail, unlockCondition }`
- [ ] `src/components/EndingGallery.vue` — 结局缩略图网格:已解锁显示画面,未解锁显示?剪影 + 提示
- [ ] 通关后自动跳转到结局画廊(或主菜单入口)
**13d. 全局统计:**
- [ ] `engine/systems/StatsSystem.ts` — 计数:线索收集数、总死亡/失败次数、各结局达成率
- [ ] `src/components/StatsPanel.vue` — 通关后展示统计面板:"73% 的玩家选择了救人"、"你收集了 3/5 个线索"
- [ ] 注:全局百分比需要后端聚合数据(`/api/stats`),纯本地可展示个人计数
**13e. 章节剧情回顾:**
- [ ] `src/components/ChapterRecap.vue` — 章节结束后显示本分支的简化流程图(已走过的路径高亮)
- [ ] 基于 SceneGraph 现有的 Vue Flow 节点图实现,只读模式
**验证:** 成就解锁弹出 toast、结局画廊正确显示解锁状态、章节结束后显示回顾、统计面板数据正确
### P14 沉浸感提升 — SFX 音效 + 对话轮 UI + 氛围特效 + 动态字幕(待实现)
目标:提升视听表现力,从"视频播放器"进化到"电影级游戏"。
**子功能清单:**
**14a. 独立音效层 (SFX)**
- [ ] `engine/systems/AudioSystem.ts` 升级 — 支持多轨道BGM 轨道 + SFX 轨道(可叠加),各自独立音量
- [ ] `SceneNode.sfx` — 场景音效定义:`[{ url, trigger: 'enter' | 'choice_a' | 'qte_success', volume }]`
- [ ] `engine/core/Engine.ts` — 事件触发对应 SFX 播放进入场景、做选择、QTE 结果)
**14b. 对话轮 UI**
- [ ] `src/components/DialogueWheel.vue` — 替代/补充底部选项列表:圆环布局,选项按弧度分布,中心显示倒计时
- [ ] 键盘/手柄方向键对应轮盘位置(↑=上方选项,↓=下方选项)
- [ ] `Choice.wheelPosition?: number` — 手动指定在轮盘中的角度0~360
**14c. 氛围特效:**
- [ ] `src/components/AmbientEffects.vue` — 全屏 CSS filter 叠加(`brightness`/`contrast`/`saturate` 渐变)
- [ ] 画面震动:`CSS transform: translate(random)` 关键帧动画
- [ ] `SceneNode.effects?: AmbientEffect[]` — 场景级特效声明(震动、暗角、色彩偏移、闪光)
**14d. 动态字幕:**
- [ ] `engine/types.ts``SubtitleCue.speaker?: string`, `SubtitleCue.color?: string`, `SubtitleCue.position?: 'left' | 'center' | 'right'`
- [ ] `src/components/Subtitles.vue` — 升级:说话人标签前缀 + 颜色区分 + 根据 position 调整水平偏移
**验证:** SFX 不打断 BGM、对话轮方向键选择、震动特效不卡顿、说话人颜色区分清晰
### P15 平台化 — 云存档 + 可访问性 + 自适应码率(待实现)
目标:面向分发和用户多样性的补全功能。
**子功能清单:**
**15a. 云存档(需后端):**
- [ ] 存档上传/下载 API 接口设计REST: `PUT /saves/:slot`, `GET /saves`
- [ ] `engine/systems/SaveSystem.ts` 升级 — save/load 支持 `remote: boolean` 参数
- [ ] 登录态管理(可选:不强制登录,本地存档为主,云存档为可选项)
**15b. 可访问性设置:**
- [ ] 字幕样式自定义:字体大小、背景透明度、颜色(白/黄/绿)
- [ ] 高对比度模式(`filter: contrast(1.3)` 全局应用)
- [ ] QTE 辅助模式:放宽时间限制、简化按键(如单键替代组合键)
- [ ] 色盲模式:选项颜色不依赖红/绿区分
- [ ] `src/components/AccessibilitySettings.vue` — 可访问性设置面板
**15c. 自适应码率:**
- [ ] `engine/core/VideoManager.ts` — 支持 HLS`.m3u8`)和 DASH`.mpd`)流媒体源
- [ ] `SceneNode.videoUrl` 支持多码率:`{ auto: '/videos/hls/scene.m3u8', hd: '/videos/scene_1080p.mp4' }`
- [ ] 网络质量检测(`navigator.connection` API自动降级
**15d. 其余补充:**
- [ ] 主菜单界面:新游戏 / 继续 / 章节选择 / 成就 / 结局画廊 / 设置
- [ ] 设置持久化到 localStorage语言、音量、可访问性偏好
- [ ] 导演评论音轨开关(`SceneNode.commentaryUrl?: string`,作为可选第二音轨)
**验证:** 云存档跨设备同步、字幕大小调整生效、码率自适应切换无卡顿
## 依赖清单
```json
{
"dependencies": {
"vue": "^3.4",
"pinia": "^2.1",
"@vue-flow/core": "^1.x",
"@vue-flow/background": "^1.x",
"@vue-flow/controls": "^1.x",
"dexie": "^4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0",
"typescript": "^5.3",
"vite": "^5.0",
"vue-tsc": "^2.0"
}
}
```
## 关键架构决策记录
1. **引擎与 UI 分离**: `engine/` 下纯 TS 类,不 import Vue。UI 层通过 composables 桥接。
2. **A/B 双缓冲**: 两个 `<video>` 元素轮换,一个播放时另一个预加载候选视频。
3. **JSON 驱动**: 所有剧情数据放在 JSON 中,编辑器本质是 JSON 的可视化读写工具。
4. **IndexedDB 存档**: 比 localStorage 容量大,可存储截屏缩略图。