Files
tianshu-engine/ROADMAP.md
cocos02 319a379921 feat: P2 - QTE system, subtitles, save thumbnails
- QTESystem: trigger detection via timeupdate, multi-key matching, timeout handling
- QTEOverlay: SVG countdown ring + key prompts + success/fail animation
- Engine: integrate QTE (timeupdate check, conditional branching, effect application)
- Subtitles: WebVTT parsing + synchronized subtitle rendering
- GamePlayer: overlay QTE and subtitle components
- SaveSystem: DB v2 with thumbnail field, canvas snapshot at 320x180 JPEG
- SaveLoadMenu: thumbnail preview for save slots
- VideoManager: getActiveVideoElement() for canvas capture
- App.vue: QTE/subtitle integration, thumbnail capture on save
- stores: QTE state management, save list with thumbnails
- demo.json: QTE scene (right_door), subtitles, new event types
- ROADMAP: mark P2 as completed
2026-06-07 19:35:14 +08:00

207 lines
7.8 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 周)
- [ ] 编辑器入口:独立 `editor/index.html` + `editor/main.ts`
- [ ] `editor/components/SceneGraph.vue` — Vue Flow 节点图(节点=场景,边=选择分支)
- [ ] `editor/components/NodeEditor.vue` — 右侧面板编辑选中节点的视频、选项、QTE、条件/效果
- [ ] `editor/components/PreviewPanel.vue` — 嵌入播放器,实时预览当前编辑的剧情线
- [ ] `editor/composables/useGraphEditor.ts` — 图数据与 JSON 双向同步
- [ ] JSON 导出/导入
- [ ] 验证:编辑器能产出合法 JSON引擎能正确加载并运行
## 依赖清单
```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 容量大,可存储截屏缩略图。