- editor/: stand-alone Vite multi-page app for visual scenario editing - editor/components/SceneGraph.vue: Vue Flow graph with scene nodes, branch/default/QTE edges - editor/components/NodeEditor.vue: right panel editing video/subtitle paths, choices, QTE params - editor/components/PreviewPanel.vue: embedded video player previewing selected scene - editor/composables/useGraphEditor.ts: bidirectional graph<->JSON sync - editor/App.vue: toolbar (new scene, import/export JSON, load demo, start scene selector) - @vue-flow/core|background|controls: graph visualization dependencies - vite.config.ts: multi-page build (main + editor) - ROADMAP: mark P3 as completed
209 lines
8.1 KiB
Markdown
209 lines
8.1 KiB
Markdown
# 交互式电影游戏引擎 — 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` — 集成 QTE(timeupdate 检测 + 条件跳转 + 效果应用)
|
||
- [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,引擎能正确加载并运行
|
||
|
||
## 依赖清单
|
||
|
||
```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 容量大,可存储截屏缩略图。
|