引擎开发约定与参考
本文档记录交互电影游戏引擎的开发规范、架构约定和工作流程,供后续开发对话中引用。
工作流程
| 规则 |
说明 |
| 先讨论再执行 |
功能实现前必须先讨论方案,确认后再写代码 |
| 先更新 ROADMAP 再实现 |
新功能先写入 ROADMAP 的 P 条目,附实现清单,再逐项完成 |
| 不自动提交 |
代码写完不要 git commit / git push,等用户检查通过后再操作 |
| 验证方式 |
npx vue-tsc --noEmit + npx vite build 通过视为基本验证 |
| 生成测试数据 |
新功能要生成配套的示例视频 / 音频 / JSON 数据 |
架构原则
| 原则 |
说明 |
| 引擎与 UI 分离 |
engine/ 下纯 TS 类,不 import Vue。UI 层通过 composables 桥接 |
| A/B 双缓冲 |
两个 <video> 元素轮换,一个播放时另一个预加载候选场景 |
| JSON 驱动 |
所有剧情数据放在 JSON 中,编辑器本质是 JSON 的可视化读写工具 |
| IndexedDB 存档 |
比 localStorage 容量大,可存储截屏缩略图。多槽位支持 |
| 故事图与玩家树 |
创作端 JSON 是有向图,展示端 BFS 投影为树(汇聚节点复制展示) |
目录结构
代码约定
| 规则 |
示例 |
| 不要写注释 |
代码自解释,不添加多余的中英文注释 |
| Engine 事件驱动 |
this.emit('sceneChange', scene) → composable 响应 |
| composable 桥接 |
useGameEngine.ts 是 Engine 和 Vue store 的中间层 |
| i18n 双文件分层 |
src/locales/ 存 UI 文本,public/demo/locales/ 存故事文本。useI18n.t() 先查故事消息,fallback UI 消息 |
| 选择翻译在 composable 中 |
choiceRequest 事件触发时 composable 调用 t(textKey) 翻译后存入 store |
| 引擎不感知 i18n |
Choice.textKey 和 Hotspot.labelKey 是数据层字段,翻译完全在 Vue 层完成 |
| demo 素材按场景分目录 |
public/demo/<scene_id>/<file> |
| demo 的 assetBase |
"assetBase": "demo/" — 所有资源路径以此为前缀 |
菜单系统
| 菜单 |
设计 |
| 主菜单 (MainMenu.vue) |
竖排单列,开始游戏最大、继续次之、底部小字装饰行(故事进度 · 成就 · 设置) |
| 暂停菜单 (PauseMenu.vue) |
ESC 弹出的全屏暂停,包含继续 / 存档 / 设置 / 返回主菜单 |
| 设置面板 (AccessibilitySettings.vue) |
语言 + 画质(仅 Web) + 字幕 + QTE 辅助 + 防误触 + 可暂停 |
| 游戏内顶栏 |
精简:跳过(条件显示) · 倍速(条件显示) · 全屏 · ≡菜单 |
视频播放模式
| 模式 |
检测方式 |
使用的 URL 字段 |
切换方式 |
| Electron 桌面 |
window.__ELECTRON__ === true |
videoUrl(本地 MP4) |
不切换,单文件 |
| Web 浏览器 |
__ELECTRON__ undefined |
streamingUrl[quality](CDN HLS) |
设置面板手动选择超清/高清/标清 |
| 画质切换 |
仅在视频播放中切换(video.ended === false),结束后不切换等待下一场景 |
|
|
关键方法:
VideoManager.resolveVideoUrl(scene, quality) — 环境检测 + URL 选择
VideoManager.switchQuality(src, seekTime) — 实时切换画质
SceneManager.getCandidateSceneIds() — 返回候选场景 ID,Engine 用 resolveVideoUrl 解析为 URL
- 预加载也走
resolveVideoUrl,Web 模式禁止预加载本地 MP4
场景 JSON 约定
| 规则 |
说明 |
| 单文件包含全部场景 |
不拆分章节为独立 JSON 文件 |
章节用 startScene 标记 |
无 endScene,BFS 遍历可达场景作为章节范围 |
| 影像场景标记 |
"type": "image", videoUrl 为空 |
| QTE 配置 |
effects.success / effects.fail 中附加变量修改,供成就检测 |
| 结局归属 |
通过 BFS 自动推导 ending.sceneId 属于哪个章节,不显式声明 chapterId |
成就系统
- 纯变量检测,在
StateManager.apply 末尾 onAfterApply 单一检查点触发
- 事件型成就改写为变量型:QTE 成功 → effects 中
set: qte_succeeded, value: 1
- 解锁时有 toast 弹出、持久化写入 IndexedDB
achievements 表
流媒体 / HLS 约定
- 每场景三档 HLS,目录结构:
public/demo/<scene>/<quality>p/index.m3u8 + seg_000.ts
demo.json 中每场景配 streamingUrl: { "超清 (1080P)": "...", "高清 (720P)": "...", "标清 (480P)": "..." }
- 生成命令:
ffmpeg -i source.mp4 -c:v libx264 -b:v <bitrate>k -c:a aac -b:a 128k -hls_time 2 -hls_segment_filename <dir>/seg_%03d.ts <dir>/index.m3u8
pack-html.cjs 跳过 videos/ 目录(Web 版使用 CDN 流媒体)
pack-mac / pack-win 保留完整视频文件(桌面版使用本地 MP4)