Files
tianshu-engine/ROADMAP.md
cocos02 937e45c203 feat: P1 core - seamless video switching, conditional branches, save/load
- VideoManager: A/B dual-buffered video with crossfade transitions and candidate preloading
- Engine: condition-based choice filtering, ChoiceSystem timer, resumeScene for save/load
- SceneManager: getCandidateUrls for preloading next scenes
- SaveSystem: Dexie.js IndexedDB multi-slot save/load
- ChoiceSystem: timed choices with countdown and auto-default on timeout
- GamePlayer: dual video elements with crossfade CSS
- ChoicePanel: timer progress bar and countdown text
- SaveLoadMenu: save/load UI component
- App.vue: menu trigger, dual video refs, save/load integration
- gameStore: timer state, saves list
- demo.json: conditional choice example (secret ending, requires trust >= 80)
- ROADMAP: mark P1 as completed
2026-06-07 16:48:52 +08:00

7.4 KiB
Raw Blame History

交互式电影游戏引擎 — 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

场景数据格式

// 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

目标:能播放一段视频 → 弹出选项 → 跳到下一段视频

  • 项目脚手架Vite + Vue3 + TypeScript + Pinia
  • engine/core/Engine.ts — 主循环骨架(加载场景 → 播放 → 等选择 → 切换)
  • engine/core/SceneManager.ts — 加载 JSON按 ID 查找场景节点
  • engine/core/VideoManager.ts — 单 video 元素播放,监听 ended 事件
  • engine/core/StateManager.ts — 变量存取、条件求值、效果执行
  • engine/types.ts — 类型定义
  • src/components/GamePlayer.vue — 挂载 video控制播放
  • src/components/ChoicePanel.vue — 渲染选择按钮,触发引擎切换
  • public/scenes/demo.json — 编写一段简单剧情7 个场景节点)
  • 验证:从 demo.json 加载场景,能走通 开始→选择→分支播放→结束 流程

P1 核心 — 无缝切换 + 条件分支 + 存档1-2 周) 已完成 2026-06-07

  • engine/core/VideoManager.ts 升级 — A/B 双缓冲预加载候选视频CSS 交叉淡化
  • engine/core/SceneManager.ts 升级 — 支持条件分支(根据 variables/flags 过滤选项)
  • engine/systems/SaveSystem.ts — Dexie.js IndexedDB 存取,多槽位
  • engine/systems/ChoiceSystem.ts — 限时选择倒计时,超时默认选择
  • src/components/SaveLoadMenu.vue — 存档/读档 UI
  • src/stores/gameStore.ts — Pinia 全局状态管理(含计时器、存档列表)
  • src/composables/useGameEngine.ts — 桥接层(双 video、存档、计时器
  • src/components/GamePlayer.vue — 双 video 元素 + 交叉淡化 CSS
  • src/components/ChoicePanel.vue — 倒计时进度条 + 计时文字
  • src/App.vue — 整合 SaveLoadMenu、双 video、计时器
  • 验证:条件分支走通,存档读档正常,视频切换交叉淡化

P2 进阶 — QTE + 字幕 + 多存档槽1 周)

  • engine/systems/QTESystem.ts — QTE 触发、键盘监听、超时判定
  • src/components/QTEOverlay.vue — QTE 视觉遮罩(按键提示 + 倒计时环)
  • src/components/Subtitles.vue — WebVTT 解析 + 字幕渲染
  • 多存档槽位 + 存档缩略图canvas 截图当前视频帧)
  • engine/core/Engine.ts — 完整事件总线sceneChange, choiceMade, qteTriggered 等)
  • 验证QTE 正常触发与判定,字幕同步,多存档正常工作

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引擎能正确加载并运行

依赖清单

{
  "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 容量大,可存储截屏缩略图。