Files
tianshu-engine/AGENTS.md

120 lines
6.7 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 再实现** | 新功能先写入 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.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()` — 返回候选场景 IDEngine 用 `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