Files
tianshu-engine/AGENTS.md

6.7 KiB
Raw Permalink Blame History

引擎开发约定与参考

本文档记录交互电影游戏引擎的开发规范、架构约定和工作流程,供后续开发对话中引用。

工作流程

规则 说明
先讨论再执行 功能实现前必须先讨论方案,确认后再写代码
先更新 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 投影为树(汇聚节点复制展示)

目录结构

moviegame/
├── engine/                     # 框架无关的核心引擎(纯 TS
│   ├── core/
│   │   ├── Engine.ts           # 主循环,驱动各子系统
│   │   ├── SceneManager.ts     # 剧情节点图遍历、章节管理
│   │   ├── VideoManager.ts     # A/B 双缓冲视频播放 + 流媒体质量选择
│   │   └── StateManager.ts     # 全局状态、变量条件求值、效果执行
│   ├── systems/
│   │   ├── ChoiceSystem.ts     # 限时选择 + 倒计时
│   │   ├── QTESystem.ts        # QTE 触发、键盘监听、超时判定
│   │   ├── AudioSystem.ts      # Web Audio API BGM + Ducking
│   │   ├── AchievementSystem.ts # 纯变量成就检测 + 解锁
│   │   └── SaveSystem.ts       # IndexedDB 多表持久化
│   └── types.ts                # 全部类型定义
├── src/
│   ├── components/             # Vue 组件——玩家端 UI + 编辑器 UI
│   ├── composables/            # 引擎 ↔ UI 桥接
│   ├── stores/                 # Pinia 全局状态
│   └── locales/                # UI 文本翻译(静态 import构建时打包
├── editor/                     # Vue Flow 可视化编辑器(独立入口)
├── electron/                   # Electron 桌面应用打包
├── public/
│   └── demo/                   # 示例素材——按场景分目录,每场景含视频、字幕、缩略图
│       └── locales/            # 故事文本翻译(动态 fetch 加载,制作者维护)
├── docs/
│   ├── SCENE_JSON_SPEC.md      # 场景 JSON 完整字段参考
│   └── ARCHITECTURE.md         # 关键架构决策记录
├── scripts/                    # 构建与打包脚本
├── ROADMAP.md                  # 待实现功能清单
└── CHANGELOG.md                # 功能更新日志

代码约定

规则 示例
不要写注释 代码自解释,不添加多余的中英文注释
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.textKeyHotspot.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() — 返回候选场景 IDEngine 用 resolveVideoUrl 解析为 URL
  • 预加载也走 resolveVideoUrlWeb 模式禁止预加载本地 MP4

场景 JSON 约定

规则 说明
单文件包含全部场景 不拆分章节为独立 JSON 文件
章节用 startScene 标记 endSceneBFS 遍历可达场景作为章节范围
影像场景标记 "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