From 6924a6f5bf910763d651d8717314d68424d10046 Mon Sep 17 00:00:00 2001 From: "lisheng.lisheng" Date: Mon, 22 Jun 2026 22:58:10 +0800 Subject: [PATCH 1/2] feat: add novel-game skill for interactive fiction generation Converts novels/stories into browser-based interactive fiction games (React SPA) with AI-generated video portraits, cutscenes, programmatic audio, and branching storylines. Uses `bl` CLI for video generation and `bl text chat` for LLM-assisted story design. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 3 +- skills/novel-game/README.md | 57 +++++ skills/novel-game/README.zh.md | 56 +++++ skills/novel-game/SKILL.md | 394 +++++++++++++++++++++++++++++++++ 4 files changed, 509 insertions(+), 1 deletion(-) create mode 100644 skills/novel-game/README.md create mode 100644 skills/novel-game/README.zh.md create mode 100644 skills/novel-game/SKILL.md diff --git a/.gitignore b/.gitignore index 842b679..11a8af9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ node_modules/ .agents/ .skills.json -skills-lock.json \ No newline at end of file +skills-lock.json +.claude/ \ No newline at end of file diff --git a/skills/novel-game/README.md b/skills/novel-game/README.md new file mode 100644 index 0000000..0d631aa --- /dev/null +++ b/skills/novel-game/README.md @@ -0,0 +1,57 @@ +# novel-game + +> [中文版 / Chinese →](README.zh.md) + +Turn novels and stories into browser-based interactive fiction games +(React SPA) — with AI-generated video portraits, cutscenes, +programmatic audio, and branching storylines. + +## Features + +- **Branching narrative engine** — multiple endings, flags system, collectible archives +- **AI asset generation** — character portraits and cutscene videos via `bl video generate` / `bl video ref` +- **LLM-assisted writing** — plot extraction, scene text generation, video prompt authoring via `bl text chat` +- **Programmatic audio** — BGM and SFX generated entirely with Web Audio API, zero external dependencies +- **Multiple UI themes** — pixel art / cyberpunk / ink-wash Chinese / minimal modern + +## Prerequisites + +- [Alibaba Cloud Model Studio CLI (`bl`)](https://bailian.aliyun.com/cli/install.md) — AI asset generation and LLM calls +- Node.js + npx — React project initialization + +## Usage + +In Claude Code (or any skill-aware agent), type: + +``` +/novel-game Adapt "The Three-Body Problem" into an interactive novel, cyberpunk style, 30-minute playthrough +``` + +The skill will: +1. Ask key design decisions (game type, UI style, length, AI assets) +2. Design the story structure with branching choices +3. Scaffold a React project with all components +4. Generate AI video assets (portraits + cutscenes) via `bl` CLI +5. Launch a dev server for browser testing + +## Project structure (generated) + +``` +src/ +├── components/ # UI components (TypeWriter, GameScene, ChoicePanel, ...) +├── data/ # Story graph, characters, archives +├── hooks/ # useGameState (reducer + hash routing), useAudio (Web Audio) +└── styles/ # Theme CSS (cyberpunk, pixel, ink-wash, ...) +scripts/ +└── generate-assets.sh # bl CLI asset generation script +public/assets/ # Downloaded AI-generated video files +``` + +## How it works + +- **Story data** (`story.js`) defines a scene graph — each scene has text segments, choices with flag mutations, and optional cutscene/archive triggers +- **Game state** is managed via `useReducer` with hash-based routing for scene navigation +- **Character portraits** are 5-second looping videos (720P) displayed in a sidebar; fallback to text initials when videos aren't available +- **Cutscenes** are 5-second 1080P videos played as full-screen overlays at key story moments +- **BGM** uses fixed MIDI pitch arrays with multi-voice layering, convolution reverb, and ADSR envelopes +- **SFX** includes noise-pulse typing sounds, sweep + chord archive unlocks, and arpeggio scene transitions diff --git a/skills/novel-game/README.zh.md b/skills/novel-game/README.zh.md new file mode 100644 index 0000000..75dcfa5 --- /dev/null +++ b/skills/novel-game/README.zh.md @@ -0,0 +1,56 @@ +# novel-game + +> [English version →](README.md) + +将小说/故事改编为浏览器端的 H5 互动小说游戏(React SPA)—— +含 AI 生成的动态角色立绘、过场动画、程序化音频和分支剧情引擎。 + +## 功能 + +- **分支剧情引擎** — 多结局、flags 系统、档案收集 +- **AI 素材生成** — 通过 `bl video generate` / `bl video ref` 生成角色立绘和过场动画 +- **LLM 辅助创作** — 通过 `bl text chat` 辅助剧情提取、场景文本生成、视频 prompt 编写 +- **程序化音频** — Web Audio API 生成 BGM 和音效,零外部依赖 +- **多种 UI 风格** — 像素风 / 赛博朋克 / 水墨中国风 / 简约现代 + +## 依赖 + +- [阿里云百炼 CLI (`bl`)](https://bailian.aliyun.com/cli/install.md) — AI 素材生成和 LLM 调用 +- Node.js + npx — React 项目初始化 + +## 使用 + +在 Claude Code(或其他支持 skill 的 agent)中输入: + +``` +/novel-game 把《三体》改编为互动小说游戏,赛博朋克风格,30分钟时长 +``` + +skill 会自动: +1. 确认关键设计决策(游戏类型、UI 风格、时长、AI 素材) +2. 设计分支剧情结构 +3. 搭建 React 项目及全部组件 +4. 通过 `bl` CLI 生成 AI 视频素材(立绘 + 过场动画) +5. 启动 dev server 在浏览器中测试 + +## 生成的项目结构 + +``` +src/ +├── components/ # UI 组件(TypeWriter、GameScene、ChoicePanel 等) +├── data/ # 场景图、角色数据、档案数据 +├── hooks/ # useGameState(reducer + hash 路由)、useAudio(Web Audio) +└── styles/ # 主题样式(赛博朋克、像素风、水墨风等) +scripts/ +└── generate-assets.sh # bl CLI 素材生成脚本 +public/assets/ # 下载到本地的 AI 生成视频文件 +``` + +## 工作原理 + +- **场景数据**(`story.js`)定义场景图——每个场景包含文本段落、带 flag 变更的选择项、可选的过场动画/档案触发 +- **游戏状态** 通过 `useReducer` 管理,结合 hash 路由实现场景导航 +- **角色立绘** 为 5 秒循环视频(720P),在侧边栏展示;视频不可用时回退为文字首字占位 +- **过场动画** 为 5 秒 1080P 视频,在关键剧情节点以全屏覆盖层播放 +- **BGM** 使用固定 MIDI 音高数组,多声部叠加、卷积混响、ADSR 包络 +- **音效** 包含噪声脉冲打字音、扫频+和弦档案解锁音、琶音场景转场音 diff --git a/skills/novel-game/SKILL.md b/skills/novel-game/SKILL.md new file mode 100644 index 0000000..3428505 --- /dev/null +++ b/skills/novel-game/SKILL.md @@ -0,0 +1,394 @@ +--- +name: novel-game +description: >- + 将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材、程序化音频、分支剧情引擎。 + 使用 `bl` CLI 完成视频素材生成(`bl video generate` / `bl video ref`)和 + LLM 辅助剧情设计(`bl text chat`)。当用户提到互动小说、文字冒险游戏、 + 小说改编游戏、H5 互动游戏、分支剧情、视觉小说时激活。 +--- + +你是一个专业的游戏策划兼全栈开发者,擅长将小说或故事改编为浏览器端的互动小说游戏。 + +用户的需求:$ARGUMENTS + +--- + +## 前置依赖 + +本技能依赖阿里云百炼 CLI(`bl`)进行 AI 素材生成和 LLM 辅助创作。使用前请检查: + +```bash +bl --version +``` + +如果未安装,请参考安装文档:https://bailian.aliyun.com/cli/install.md + +--- + +## 第一步:需求收集 + +使用 AskUserQuestion 向用户确认以下关键设计决策(一次性问完): + +1. **素材来源** — 用户是否提供了小说文件(EPUB/TXT)?如果有,先读取内容提取剧情结构。 +2. **游戏类型** — 互动小说(选择影响剧情) / 文字冒险+解谜 / 文字RPG(含属性系统) +3. **UI 风格** — 像素风 / 赛博朋克 / 水墨中国风 / 简约现代 +4. **叙事视角** — 第一人称(扮演主角) / 第三人称上帝视角(旁观者选择)/ 双主角切换 +5. **AI 素材生成** — 是否需要 AI 生成角色立绘和过场动画?(使用 `bl video generate` / `bl video ref`) +6. **音频** — 无音频 / 仅 BGM / BGM + 音效(全部用 Web Audio API 程序化生成,零外部依赖) +7. **游戏时长** — 15-20分钟(8-10场景)/ 30-45分钟(15-18场景)/ 1小时+(25+场景) + +--- + +## 第二步:剧情设计 + +### 使用 `bl text chat` 辅助剧情提取与设计 + +如果用户提供了小说原文,使用 `bl text chat` 提取剧情结构: + +```bash +# 从小说文件提取核心剧情线、关键分支点、角色列表 +bl text chat \ + --system "你是专业的互动小说策划师,擅长从原著中提取适合改编为互动游戏的剧情结构。" \ + --message "以下是小说原文:$(cat novel.txt)" \ + --message "请提取:1) 1-3条主线剧情 2) 3-5个适合做分支选择的关键节点 3) 主要角色列表(含外观描述)4) 3-5个可能的不同结局 5) 适合做过场动画的高潮场景" \ + --max-tokens 8192 +``` + +```bash +# 根据大纲生成具体的场景文本和选择 +bl text chat \ + --system "你是互动小说编剧,擅长写引人入胜的场景描写和有意义的分支选择。" \ + --message "根据以下剧情大纲,为每个场景生成详细的叙事文本和 2-3 个分支选择(含对应 flag 设置):$(cat outline.md)" \ + --max-tokens 8192 +``` + +```bash +# 生成 AI 视频素材的 prompt +bl text chat \ + --system "你是 AI 视频生成的 prompt 专家,擅长写出能产生高质量视频的描述。" \ + --message "为以下互动小说场景生成视频 prompt(中文,每个 50-100 字,描述画面、动作、氛围):$(cat scenes.md)" \ + --max-tokens 4096 +``` + +### 剧情结构产出物 + +根据原著/素材提取: + +1. **核心剧情线** — 识别 1-3 条主线(可交织),每条线梳理关键场景 +2. **关键分支点** — 选出 3-5 个影响结局的重大选择 +3. **结局设计** — 设计 3-5 个不同结局,每个由 flags 组合决定 +4. **角色列表** — 列出需要立绘的主要角色(6-8个) +5. **过场动画** — 列出需要生成视频的高潮场景(5-8个) +6. **收集物/档案** — 设计通过选择解锁的背景知识条目 + +--- + +## 第三步:项目架构 + +使用 `npx create-react-app` 初始化,按以下结构组织代码: + +``` +src/ +├── App.jsx # 主应用,游戏状态路由 +├── index.js +├── components/ +│ ├── TitleScreen.jsx # 开始画面(主题动画+标题) +│ ├── GameScene.jsx # 核心场景渲染(文本+选择+立绘) +│ ├── TypeWriter.jsx # 打字机逐字显示效果 +│ ├── ChoicePanel.jsx # 选择面板(hover动效+延迟入场) +│ ├── CharacterPortrait.jsx # 角色立绘(视频/图片/首字占位) +│ ├── CutScene.jsx # 过场动画播放 +│ ├── EndingScreen.jsx # 结局界面 +│ ├── ArchivePanel.jsx # 档案/收集物面板 +│ ├── ArchiveNotification.jsx # 档案解锁通知 +│ └── ProgressBar.jsx # 章节进度条 +├── data/ +│ ├── story.js # 场景图(核心数据) +│ ├── characters.js # 角色定义 +│ ├── archives.js # 档案数据 +│ └── generated-assets.json # AI 生成素材的本地路径 +├── hooks/ +│ ├── useGameState.js # useReducer 游戏状态管理 +│ └── useAudio.js # Web Audio API 音频系统 +├── styles/ +│ ├── pixel.css # 主题样式(根据用户选择调整) +│ ├── animations.css # 动画定义 +│ └── crt.css # CRT 扫描线效果(像素风专用) +└── [特殊场景组件] # 按需:Canvas 动态背景等 +scripts/ +└── generate-assets.sh # bl CLI 素材生成脚本 +public/ +└── assets/ # 下载到本地的素材文件 + ├── portraits/ + └── cutscenes/ +``` + +--- + +## 第四步:核心数据模型 + +### story.js 场景数据结构 + +```js +export const scenes = { + "scene_id": { + id: "scene_id", + title: "章节标题", + timeline: "past|present|game", // 时间线标识(影响 UI 颜色) + year: "1967", // 显示年份 + character: "character_key", // 当前场景角色立绘 + bgm: "bgm_name", // 背景音乐 + cutscene: "cutscene_key", // 过场动画(可选) + isEnding: false, // 是否为结局场景 + endingType: "ending_a", // 结局类型标识 + texts: [ // 逐段显示的文本 + "第一段文字...", + "第二段文字..." + ], + choices: [ // 最后一段文字后出现的选择 + { + text: "选择A的文字", + next: "next_scene_id", // 跳转目标(null = 根据 flags 进入结局) + setFlags: { flag_name: true }, // 设置标记 + archive: "archive_key" // 解锁档案(可选) + } + ] + } +}; + +export function getEnding(flags) { + // 根据 flags 组合返回对应结局 scene id +} +``` + +### useGameState 状态结构 + +```js +{ + phase: 'title' | 'playing' | 'cutscene' | 'ending', + currentScene: string, // 当前场景 ID + flags: {}, // 玩家选择累积的标记 + history: string[], // 已访问场景 + archives: string[], // 已解锁档案 + textIndex: number, // 当前场景第几段文本 + typingDone: boolean, // 打字机是否完成当前段 + showCutscene: boolean, + showArchive: boolean, + newArchive: null | string, // 新解锁档案通知 + bgm: string | null, +} +``` + +--- + +## 第五步:关键实现模式 + +### 打字机效果(TypeWriter) +- 用 setInterval 逐字显示,speed 约 40-50ms +- 点击可跳过(立即显示全文) +- 每个字符触发打字音效回调 +- 显示完毕调用 onDone 回调 + +### 选择面板(ChoicePanel) +- 在最后一段文字打字完成后淡入 +- 每个选项延迟入场动画(nth-child animation-delay) +- hover 时边框变色 + 微位移 + 阴影扩大 +- 点击触发音效 → 设置 flags → 跳转下一场景 + +### Hash 路由 +- URL hash 同步当前场景:`#scene_id` +- 支持直接通过 URL 跳转到任意章节(开发调试 + 分享) +- 监听 hashchange 支持浏览器前进/后退 +- 回到标题时清除 hash + +### AI 素材生成(使用 `bl` CLI,⚠️ 必须下载到本地) + +使用 Shell 脚本 `scripts/generate-assets.sh` 调用 `bl` CLI 生成并下载素材。`bl video generate` 的 `--download` 标志会自动轮询任务状态直到完成并下载文件,无需手写轮询逻辑。 + +#### 角色立绘(有参考图 → image-to-video) + +```bash +# 从角色参考图生成动态立绘 +bl video generate \ + --image ./references/character_a.png \ + --prompt "一个年轻女子微微转头,长发随风飘动,表情温柔,半身特写,电影质感光影" \ + --resolution 720P \ + --duration 5 \ + --watermark false \ + --download public/assets/portraits/character_a.mp4 +``` + +#### 角色立绘(无参考图 → text-to-video) + +```bash +# 纯文本描述生成角色立绘 +bl video generate \ + --prompt "水墨画风格,一位身穿白色长袍的青年剑客站在竹林中,风吹竹叶,衣袂飘飘,半身特写" \ + --resolution 720P \ + --duration 5 \ + --watermark false \ + --download public/assets/portraits/swordsman.mp4 +``` + +#### 过场动画 + +```bash +# 生成高潮场景过场动画 +bl video generate \ + --prompt "暴风雨中的悬崖边,闪电划破夜空,两个人影对峙,镜头从远景缓慢推近到中景,电影级画质" \ + --resolution 1080P \ + --duration 5 \ + --watermark false \ + --download public/assets/cutscenes/cliff_confrontation.mp4 +``` + +#### 多角色场景(使用 `bl video ref` 保持角色一致性) + +```bash +# 多角色一致性场景 +bl video ref \ + --prompt "Image1 和 Image2 面对面站在古城门前,Image1 递出一封信,Image2 犹豫地伸出手,夕阳逆光" \ + --image ./references/character_a.png \ + --image ./references/character_b.png \ + --resolution 1080P \ + --duration 5 \ + --watermark false \ + --download public/assets/cutscenes/letter_scene.mp4 +``` + +#### 完整生成脚本示例 `scripts/generate-assets.sh` + +```bash +#!/bin/bash +set -e + +# 角色立绘生成 +declare -A PORTRAITS=( + ["character_a"]="一位身穿红色汉服的少女,微风中秀发飘动,温柔微笑,半身特写" + ["character_b"]="一位戴着斗笠的中年剑客,目光锐利,轻抚剑柄,半身特写" +) + +echo "=== 生成角色立绘 ===" +for name in "${!PORTRAITS[@]}"; do + outfile="public/assets/portraits/${name}.mp4" + if [ -f "$outfile" ]; then + echo " [跳过] $name — 已存在" + continue + fi + + echo " [生成] $name ..." + # 如果有参考图则用 --image + if [ -f "references/${name}.png" ]; then + bl video generate \ + --image "references/${name}.png" \ + --prompt "${PORTRAITS[$name]}" \ + --resolution 720P --duration 5 --watermark false \ + --download "$outfile" + else + bl video generate \ + --prompt "${PORTRAITS[$name]}" \ + --resolution 720P --duration 5 --watermark false \ + --download "$outfile" + fi + echo " [完成] $name → $outfile" +done + +# 过场动画生成 +declare -A CUTSCENES=( + ["opening"]="古老的卷轴缓缓展开,露出水墨山水画,镜头推进画中世界,色彩从黑白渐变为彩色" + ["climax"]="暴风雨中两人在悬崖边对峙,闪电照亮面庞,镜头围绕旋转,电影质感" +) + +echo "=== 生成过场动画 ===" +for name in "${!CUTSCENES[@]}"; do + outfile="public/assets/cutscenes/${name}.mp4" + if [ -f "$outfile" ]; then + echo " [跳过] $name — 已存在" + continue + fi + + echo " [生成] $name ..." + bl video generate \ + --prompt "${CUTSCENES[$name]}" \ + --resolution 1080P --duration 5 --watermark false \ + --download "$outfile" + echo " [完成] $name → $outfile" +done + +# 更新 generated-assets.json +echo "=== 更新 generated-assets.json ===" +node -e " +const fs = require('fs'); +const path = require('path'); +const assets = { portraits: {}, cutscenes: {} }; +for (const f of fs.readdirSync('public/assets/portraits')) { + if (f.endsWith('.mp4')) assets.portraits[path.basename(f,'.mp4')] = '/assets/portraits/' + f; +} +for (const f of fs.readdirSync('public/assets/cutscenes')) { + if (f.endsWith('.mp4')) assets.cutscenes[path.basename(f,'.mp4')] = '/assets/cutscenes/' + f; +} +fs.writeFileSync('src/data/generated-assets.json', JSON.stringify(assets, null, 2)); +console.log(' 写入完成:', JSON.stringify(assets, null, 2)); +" + +echo "=== 全部完成 ===" +``` + +#### 关键规则 +- **素材必须离线生成并下载到本地**:`bl video generate --download` 一步到位完成生成+等待+下载 +- **本地文件路径直接传入 `--image`**:`bl` CLI 自动上传到临时存储,无需手动上传 +- **已生成素材自动跳过**:脚本检查本地文件是否存在(`[ -f path ]`) +- **generated-assets.json 只存本地路径**:如 `/assets/portraits/character_a.mp4`,绝不存远程 URL +- **游戏运行时零 API 调用**:所有素材都是预生成的本地文件 + +### Web Audio API 程序化音乐 +- 用 MIDI 音高数组定义旋律乐句,循环播放 +- 多声部叠加(主旋律 + 去谐波 detune + pad 持续音) +- 卷积混响(用随机衰减 impulse buffer) +- ADSR 包络(attack-decay-sustain-release) +- 低通滤波器随时间衰减 +- 不同场景/氛围用不同配置(bpm、音阶、波形、滤波频率) + +### 音效 +- **打字音**:白噪声脉冲 + 带通滤波(2000-4000Hz)+ 微弱正弦下降音,模拟机械击键 +- **点击音**:双音方波上行(660→880Hz) +- **场景切换**:四音正弦琶音 + 混响 +- **档案解锁**:扫频 + 四音三角波和弦 + +--- + +## 第六步:开发流程 + +按以下顺序执行,每步完成后标记 task: + +1. 初始化 React 项目 + 目录结构 +2. 编写 story.js(所有场景文本、选择、分支)— 这是最大的工作量,可用 `bl text chat` 辅助生成 +3. 编写 characters.js 和 archives.js +4. 实现主题样式(CSS 变量、字体、配色、动画) +5. 实现核心组件:TypeWriter → GameScene → ChoicePanel → CharacterPortrait +6. 实现 TitleScreen + EndingScreen + ArchivePanel +7. 实现 useGameState(reducer + hash 路由) +8. 实现 useAudio(BGM 乐谱 + 音效) +9. 如需 AI 素材:编写 `scripts/generate-assets.sh` → 执行 `bash scripts/generate-assets.sh` 生成并下载素材 +10. 实现特殊场景效果(Canvas 动态背景、点击交互等) +11. 整合 App.jsx +12. 启动 dev server,浏览器测试完整流程(至少走通两条路线到不同结局) + +--- + +## 避坑指南 + +以下是从实际开发中总结的经验,务必遵循: + +- **bl video 分辨率**:`bl video generate` 支持 720P 和 1080P,测试阶段用 720P(更快更便宜),最终版用 1080P +- **bl video --download 自动轮询**:`--download` 标志会自动等待任务完成并下载文件,无需手写轮询代码;如需异步可用 `--async` 获取 task-id 后用 `bl video download --task-id --out ` 手动下载 +- **本地路径自动上传**:`bl` CLI 的 `--image` 接受本地文件路径,会自动上传到临时存储(48小时有效),无需手动上传到 OSS +- **素材必须离线生成并下载到本地**:视频生成耗时 2-5 分钟/个,绝不能在游戏运行时调用。`--download` 下载到 `public/assets/`,generated-assets.json 中只存本地路径 +- **Prompt 内容审核**:避免暴力、吸烟等敏感描述,否则会被 API 拒绝;换温和表述重试 +- **React Hooks 顺序**:所有 useCallback/useEffect 必须在 early return 之前调用,否则报 rules-of-hooks 错误 +- **BGM 编曲**:用固定 MIDI 乐谱数组循环播放,不要用随机音符漫游(听起来像噪声) +- **打字音质感**:用噪声脉冲 + 带通滤波模拟击键,比纯方波 beep 好很多 +- **特殊场景视觉**:Canvas 动态背景必须与叙事内容紧密关联(出现什么元素画什么),不能泛泛画星空了事 +- **点击交互**:Canvas 场景加粒子爆发 + 冲击波环 + 屏幕震动 + 主题相关额外效果,大幅提升沉浸感 +- **CRA 清理**:初始化后立即删除 App.css/logo.svg/setupTests.js 等样板文件,避免冲突 +- **bl text chat 辅助创作**:剧情大纲、场景文本、分支设计、视频 prompt 都可以用 `bl text chat` 辅助生成,大幅减少手写工作量 From 313f6852ae503fd8d7ba25679b5ae92f0f90b561 Mon Sep 17 00:00:00 2001 From: "lisheng.lisheng" Date: Mon, 22 Jun 2026 23:15:57 +0800 Subject: [PATCH 2/2] feat(novel-game): add image/TTS/save/mobile support, remove bl text chat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Asset mode: video / image (`bl image generate`) / mixed — user chooses - Optional TTS narration via `bl speech synthesize` - Save system: auto-save + 3 manual slots (localStorage) - Mobile-first: portrait layout, touch events, safe-area, 44px targets - Video lazy loading: preload=none, memory release on scene exit - Parallel asset generation: --async + bl video download batch mode - Ken Burns effect for image-mode cutscenes - Remove bl text chat — agent itself handles story generation Co-Authored-By: Claude Opus 4.6 (1M context) --- skills/novel-game/README.md | 30 +-- skills/novel-game/README.zh.md | 50 +++-- skills/novel-game/SKILL.md | 384 +++++++++++++++++---------------- 3 files changed, 245 insertions(+), 219 deletions(-) diff --git a/skills/novel-game/README.md b/skills/novel-game/README.md index 0d631aa..edd97ae 100644 --- a/skills/novel-game/README.md +++ b/skills/novel-game/README.md @@ -3,20 +3,22 @@ > [中文版 / Chinese →](README.zh.md) Turn novels and stories into browser-based interactive fiction games -(React SPA) — with AI-generated video portraits, cutscenes, +(React SPA) — with AI-generated video/image assets, optional TTS narration, programmatic audio, and branching storylines. ## Features - **Branching narrative engine** — multiple endings, flags system, collectible archives -- **AI asset generation** — character portraits and cutscene videos via `bl video generate` / `bl video ref` -- **LLM-assisted writing** — plot extraction, scene text generation, video prompt authoring via `bl text chat` +- **AI asset generation** — character portraits and cutscenes via video (`bl video generate` / `bl video ref`) or image (`bl image generate`), mixable +- **Optional TTS narration** — scene narration and character monologues via `bl speech synthesize` - **Programmatic audio** — BGM and SFX generated entirely with Web Audio API, zero external dependencies +- **Save system** — auto-save + 3 manual save slots (localStorage) +- **Mobile-first** — portrait layout, touch-optimized, safe-area support - **Multiple UI themes** — pixel art / cyberpunk / ink-wash Chinese / minimal modern ## Prerequisites -- [Alibaba Cloud Model Studio CLI (`bl`)](https://bailian.aliyun.com/cli/install.md) — AI asset generation and LLM calls +- [Alibaba Cloud Model Studio CLI (`bl`)](https://bailian.aliyun.com/cli/install.md) — AI asset generation (video/image/speech) - Node.js + npx — React project initialization ## Usage @@ -28,30 +30,32 @@ In Claude Code (or any skill-aware agent), type: ``` The skill will: -1. Ask key design decisions (game type, UI style, length, AI assets) +1. Ask key design decisions (game type, UI style, length, asset mode) 2. Design the story structure with branching choices 3. Scaffold a React project with all components -4. Generate AI video assets (portraits + cutscenes) via `bl` CLI +4. Generate AI assets (video/image/speech) via `bl` CLI 5. Launch a dev server for browser testing ## Project structure (generated) ``` src/ -├── components/ # UI components (TypeWriter, GameScene, ChoicePanel, ...) -├── data/ # Story graph, characters, archives -├── hooks/ # useGameState (reducer + hash routing), useAudio (Web Audio) +├── components/ # UI components (TypeWriter, GameScene, ChoicePanel, SaveLoadPanel, ...) +├── data/ # Story graph, characters, archives, asset index +├── hooks/ # useGameState (reducer + hash routing + save), useAudio (Web Audio) └── styles/ # Theme CSS (cyberpunk, pixel, ink-wash, ...) scripts/ -└── generate-assets.sh # bl CLI asset generation script -public/assets/ # Downloaded AI-generated video files +└── generate-assets.sh # bl CLI asset generation script (supports parallel generation) +public/assets/ # Downloaded AI-generated asset files ``` ## How it works - **Story data** (`story.js`) defines a scene graph — each scene has text segments, choices with flag mutations, and optional cutscene/archive triggers - **Game state** is managed via `useReducer` with hash-based routing for scene navigation -- **Character portraits** are 5-second looping videos (720P) displayed in a sidebar; fallback to text initials when videos aren't available -- **Cutscenes** are 5-second 1080P videos played as full-screen overlays at key story moments +- **Character portraits** support video (5-second looping 720P) or image (with CSS breathing animation), auto-detected +- **Cutscenes** support video (full-screen playback) or image (Ken Burns pan/zoom effect), auto-detected +- **Save system** — auto-save after each choice + 3 manual save slots in localStorage - **BGM** uses fixed MIDI pitch arrays with multi-voice layering, convolution reverb, and ADSR envelopes - **SFX** includes noise-pulse typing sounds, sweep + chord archive unlocks, and arpeggio scene transitions +- **TTS narration** (optional) — pre-generated MP3 files play automatically during scenes diff --git a/skills/novel-game/README.zh.md b/skills/novel-game/README.zh.md index 75dcfa5..a2acb73 100644 --- a/skills/novel-game/README.zh.md +++ b/skills/novel-game/README.zh.md @@ -1,21 +1,23 @@ # novel-game -> [English version →](README.md) +> [English →](README.md) 将小说/故事改编为浏览器端的 H5 互动小说游戏(React SPA)—— -含 AI 生成的动态角色立绘、过场动画、程序化音频和分支剧情引擎。 +含 AI 生成视频/图片素材、可选 TTS 旁白、程序化音频和分支剧情引擎。 ## 功能 - **分支剧情引擎** — 多结局、flags 系统、档案收集 -- **AI 素材生成** — 通过 `bl video generate` / `bl video ref` 生成角色立绘和过场动画 -- **LLM 辅助创作** — 通过 `bl text chat` 辅助剧情提取、场景文本生成、视频 prompt 编写 -- **程序化音频** — Web Audio API 生成 BGM 和音效,零外部依赖 +- **AI 素材生成** — 角色立绘和过场 CG,支持视频 (`bl video generate` / `bl video ref`) 或图片 (`bl image generate`),可混合使用 +- **可选 TTS 旁白** — 通过 `bl speech synthesize` 生成场景旁白和角色独白 +- **程序化音频** — BGM 和音效完全用 Web Audio API 生成,零外部依赖 +- **存档系统** — 自动存档 + 3 个手动存档槽位(localStorage) +- **移动端适配** — 竖屏优先、触控优化、安全区适配 - **多种 UI 风格** — 像素风 / 赛博朋克 / 水墨中国风 / 简约现代 ## 依赖 -- [阿里云百炼 CLI (`bl`)](https://bailian.aliyun.com/cli/install.md) — AI 素材生成和 LLM 调用 +- [阿里云百炼 CLI (`bl`)](https://bailian.aliyun.com/cli/install.md) — AI 素材生成(视频/图片/语音) - Node.js + npx — React 项目初始化 ## 使用 @@ -23,34 +25,36 @@ 在 Claude Code(或其他支持 skill 的 agent)中输入: ``` -/novel-game 把《三体》改编为互动小说游戏,赛博朋克风格,30分钟时长 +/novel-game 把《三体》地球往事改编为互动小说,水墨风格,30分钟时长 ``` -skill 会自动: -1. 确认关键设计决策(游戏类型、UI 风格、时长、AI 素材) +Skill 将会: +1. 询问关键设计决策(游戏类型、UI 风格、时长、素材模式) 2. 设计分支剧情结构 -3. 搭建 React 项目及全部组件 -4. 通过 `bl` CLI 生成 AI 视频素材(立绘 + 过场动画) +3. 初始化 React 项目并实现所有组件 +4. 通过 `bl` CLI 生成 AI 素材(视频/图片/语音) 5. 启动 dev server 在浏览器中测试 ## 生成的项目结构 ``` src/ -├── components/ # UI 组件(TypeWriter、GameScene、ChoicePanel 等) -├── data/ # 场景图、角色数据、档案数据 -├── hooks/ # useGameState(reducer + hash 路由)、useAudio(Web Audio) -└── styles/ # 主题样式(赛博朋克、像素风、水墨风等) +├── components/ # UI 组件(TypeWriter、GameScene、ChoicePanel、SaveLoadPanel...) +├── data/ # 场景图、角色、档案、素材索引 +├── hooks/ # useGameState(reducer + hash 路由 + 存档)、useAudio(Web Audio) +└── styles/ # 主题 CSS(赛博朋克、像素风、水墨、简约...) scripts/ -└── generate-assets.sh # bl CLI 素材生成脚本 -public/assets/ # 下载到本地的 AI 生成视频文件 +└── generate-assets.sh # bl CLI 素材生成脚本(支持并行生成) +public/assets/ # 下载到本地的 AI 生成素材文件 ``` ## 工作原理 -- **场景数据**(`story.js`)定义场景图——每个场景包含文本段落、带 flag 变更的选择项、可选的过场动画/档案触发 -- **游戏状态** 通过 `useReducer` 管理,结合 hash 路由实现场景导航 -- **角色立绘** 为 5 秒循环视频(720P),在侧边栏展示;视频不可用时回退为文字首字占位 -- **过场动画** 为 5 秒 1080P 视频,在关键剧情节点以全屏覆盖层播放 -- **BGM** 使用固定 MIDI 音高数组,多声部叠加、卷积混响、ADSR 包络 -- **音效** 包含噪声脉冲打字音、扫频+和弦档案解锁音、琶音场景转场音 +- **场景数据** (`story.js`) 定义场景图——每个场景含文本段落、带 flag 变更的分支选择、可选的过场/档案触发 +- **游戏状态** 通过 `useReducer` 管理,hash 路由实现场景导航 +- **角色立绘** 支持视频(5秒循环 720P)或图片(带呼吸动效),组件自动适配 +- **过场** 支持视频(全屏播放)或图片(Ken Burns 缓动效果),自动适配 +- **存档** 自动存档 + 3 个手动槽位,存入 localStorage +- **BGM** 使用固定 MIDI 音高数组 + 多声部叠加 + 卷积混响 + ADSR 包络 +- **音效** 包括噪声脉冲打字音、扫频档案解锁音、琶音场景切换音 +- **TTS 旁白**(可选)预生成的 MP3 文件在场景播放时自动播放 diff --git a/skills/novel-game/SKILL.md b/skills/novel-game/SKILL.md index 3428505..8254e67 100644 --- a/skills/novel-game/SKILL.md +++ b/skills/novel-game/SKILL.md @@ -1,10 +1,10 @@ --- name: novel-game description: >- - 将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材、程序化音频、分支剧情引擎。 - 使用 `bl` CLI 完成视频素材生成(`bl video generate` / `bl video ref`)和 - LLM 辅助剧情设计(`bl text chat`)。当用户提到互动小说、文字冒险游戏、 - 小说改编游戏、H5 互动游戏、分支剧情、视觉小说时激活。 + 将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材(视频/图片可选)、 + 可选 TTS 旁白、程序化音频、分支剧情引擎、存档系统。 + 使用 `bl` CLI 完成素材生成(`bl video generate` / `bl image generate` / `bl speech synthesize`)。 + 当用户提到互动小说、文字冒险游戏、小说改编游戏、H5 互动游戏、分支剧情、视觉小说时激活。 --- 你是一个专业的游戏策划兼全栈开发者,擅长将小说或故事改编为浏览器端的互动小说游戏。 @@ -15,7 +15,7 @@ description: >- ## 前置依赖 -本技能依赖阿里云百炼 CLI(`bl`)进行 AI 素材生成和 LLM 辅助创作。使用前请检查: +本技能依赖阿里云百炼 CLI(`bl`)进行 AI 素材生成(视频/图片/语音)。使用前请检查: ```bash bl --version @@ -33,52 +33,28 @@ bl --version 2. **游戏类型** — 互动小说(选择影响剧情) / 文字冒险+解谜 / 文字RPG(含属性系统) 3. **UI 风格** — 像素风 / 赛博朋克 / 水墨中国风 / 简约现代 4. **叙事视角** — 第一人称(扮演主角) / 第三人称上帝视角(旁观者选择)/ 双主角切换 -5. **AI 素材生成** — 是否需要 AI 生成角色立绘和过场动画?(使用 `bl video generate` / `bl video ref`) -6. **音频** — 无音频 / 仅 BGM / BGM + 音效(全部用 Web Audio API 程序化生成,零外部依赖) +5. **AI 素材生成** — 是否需要 AI 生成角色立绘和过场?如需要,选择素材模式: + - **视频模式** — 角色立绘为动态视频循环,过场为视频(效果最佳,生成慢,成本高) + - **图片模式** — 角色立绘为静态图片,过场为静态 CG + Ken Burns 动效(生成快,成本低) + - **混合模式**(推荐)— 角色立绘用图片省成本,关键过场用视频提升体验 +6. **音频** — 选择音频方案: + - 无音频 + - 仅 BGM(Web Audio API 程序化生成) + - BGM + 音效(全部 Web Audio API 程序化生成,零外部依赖) + - BGM + 音效 + TTS 旁白(BGM/音效程序化生成 + `bl speech synthesize` 生成旁白语音) 7. **游戏时长** — 15-20分钟(8-10场景)/ 30-45分钟(15-18场景)/ 1小时+(25+场景) --- ## 第二步:剧情设计 -### 使用 `bl text chat` 辅助剧情提取与设计 - -如果用户提供了小说原文,使用 `bl text chat` 提取剧情结构: - -```bash -# 从小说文件提取核心剧情线、关键分支点、角色列表 -bl text chat \ - --system "你是专业的互动小说策划师,擅长从原著中提取适合改编为互动游戏的剧情结构。" \ - --message "以下是小说原文:$(cat novel.txt)" \ - --message "请提取:1) 1-3条主线剧情 2) 3-5个适合做分支选择的关键节点 3) 主要角色列表(含外观描述)4) 3-5个可能的不同结局 5) 适合做过场动画的高潮场景" \ - --max-tokens 8192 -``` - -```bash -# 根据大纲生成具体的场景文本和选择 -bl text chat \ - --system "你是互动小说编剧,擅长写引人入胜的场景描写和有意义的分支选择。" \ - --message "根据以下剧情大纲,为每个场景生成详细的叙事文本和 2-3 个分支选择(含对应 flag 设置):$(cat outline.md)" \ - --max-tokens 8192 -``` - -```bash -# 生成 AI 视频素材的 prompt -bl text chat \ - --system "你是 AI 视频生成的 prompt 专家,擅长写出能产生高质量视频的描述。" \ - --message "为以下互动小说场景生成视频 prompt(中文,每个 50-100 字,描述画面、动作、氛围):$(cat scenes.md)" \ - --max-tokens 4096 -``` - -### 剧情结构产出物 - -根据原著/素材提取: +根据原著/素材/用户描述,直接设计以下内容: 1. **核心剧情线** — 识别 1-3 条主线(可交织),每条线梳理关键场景 2. **关键分支点** — 选出 3-5 个影响结局的重大选择 3. **结局设计** — 设计 3-5 个不同结局,每个由 flags 组合决定 -4. **角色列表** — 列出需要立绘的主要角色(6-8个) -5. **过场动画** — 列出需要生成视频的高潮场景(5-8个) +4. **角色列表** — 列出需要立绘的主要角色(6-8个),含外观描述 +5. **过场场景** — 列出需要生成素材的高潮场景(5-8个),含画面描述 6. **收集物/档案** — 设计通过选择解锁的背景知识条目 --- @@ -92,26 +68,27 @@ src/ ├── App.jsx # 主应用,游戏状态路由 ├── index.js ├── components/ -│ ├── TitleScreen.jsx # 开始画面(主题动画+标题) -│ ├── GameScene.jsx # 核心场景渲染(文本+选择+立绘) +│ ├── TitleScreen.jsx # 开始画面(主题动画+标题+继续游戏+存档管理) +│ ├── GameScene.jsx # 核心场景渲染(文本+选择+立绘+旁白) │ ├── TypeWriter.jsx # 打字机逐字显示效果 │ ├── ChoicePanel.jsx # 选择面板(hover动效+延迟入场) -│ ├── CharacterPortrait.jsx # 角色立绘(视频/图片/首字占位) -│ ├── CutScene.jsx # 过场动画播放 +│ ├── CharacterPortrait.jsx # 角色立绘(自动适配视频/图片) +│ ├── CutScene.jsx # 过场播放(视频/图片 Ken Burns) │ ├── EndingScreen.jsx # 结局界面 │ ├── ArchivePanel.jsx # 档案/收集物面板 │ ├── ArchiveNotification.jsx # 档案解锁通知 +│ ├── SaveLoadPanel.jsx # 存档/读档面板 │ └── ProgressBar.jsx # 章节进度条 ├── data/ │ ├── story.js # 场景图(核心数据) │ ├── characters.js # 角色定义 │ ├── archives.js # 档案数据 -│ └── generated-assets.json # AI 生成素材的本地路径 +│ └── generated-assets.json # AI 生成素材的本地路径+类型 ├── hooks/ -│ ├── useGameState.js # useReducer 游戏状态管理 +│ ├── useGameState.js # useReducer 游戏状态管理 + 存档 │ └── useAudio.js # Web Audio API 音频系统 ├── styles/ -│ ├── pixel.css # 主题样式(根据用户选择调整) +│ ├── theme.css # 主题样式(根据用户选择调整) │ ├── animations.css # 动画定义 │ └── crt.css # CRT 扫描线效果(像素风专用) └── [特殊场景组件] # 按需:Canvas 动态背景等 @@ -119,8 +96,10 @@ scripts/ └── generate-assets.sh # bl CLI 素材生成脚本 public/ └── assets/ # 下载到本地的素材文件 - ├── portraits/ - └── cutscenes/ + ├── portraits/ # 角色立绘(.mp4 或 .png) + ├── cutscenes/ # 过场素材(.mp4 或 .png) + ├── backgrounds/ # 场景背景图(.png) + └── narrations/ # TTS 旁白音频(.mp3) ``` --- @@ -138,18 +117,19 @@ export const scenes = { year: "1967", // 显示年份 character: "character_key", // 当前场景角色立绘 bgm: "bgm_name", // 背景音乐 - cutscene: "cutscene_key", // 过场动画(可选) - isEnding: false, // 是否为结局场景 - endingType: "ending_a", // 结局类型标识 - texts: [ // 逐段显示的文本 + cutscene: "cutscene_key", // 过场素材 key(可选) + narration: "narration_key", // TTS 旁白 key(可选) + isEnding: false, + endingType: "ending_a", + texts: [ "第一段文字...", "第二段文字..." ], - choices: [ // 最后一段文字后出现的选择 + choices: [ { text: "选择A的文字", next: "next_scene_id", // 跳转目标(null = 根据 flags 进入结局) - setFlags: { flag_name: true }, // 设置标记 + setFlags: { flag_name: true }, archive: "archive_key" // 解锁档案(可选) } ] @@ -161,20 +141,41 @@ export function getEnding(flags) { } ``` +### generated-assets.json 数据结构 + +```json +{ + "portraits": { + "ye_wenjie": { "path": "/assets/portraits/ye_wenjie.mp4", "type": "video" }, + "luo_ji": { "path": "/assets/portraits/luo_ji.png", "type": "image" } + }, + "cutscenes": { + "red_coast": { "path": "/assets/cutscenes/red_coast.mp4", "type": "video" }, + "countdown": { "path": "/assets/cutscenes/countdown.png", "type": "image" } + }, + "backgrounds": { + "campus_1967": { "path": "/assets/backgrounds/campus_1967.png", "type": "image" } + }, + "narrations": { + "scene_opening": { "path": "/assets/narrations/scene_opening.mp3" } + } +} +``` + ### useGameState 状态结构 ```js { phase: 'title' | 'playing' | 'cutscene' | 'ending', - currentScene: string, // 当前场景 ID - flags: {}, // 玩家选择累积的标记 - history: string[], // 已访问场景 - archives: string[], // 已解锁档案 - textIndex: number, // 当前场景第几段文本 - typingDone: boolean, // 打字机是否完成当前段 + currentScene: string, + flags: {}, + history: string[], + archives: string[], + textIndex: number, + typingDone: boolean, showCutscene: boolean, showArchive: boolean, - newArchive: null | string, // 新解锁档案通知 + newArchive: null | string, bgm: string | null, } ``` @@ -185,7 +186,7 @@ export function getEnding(flags) { ### 打字机效果(TypeWriter) - 用 setInterval 逐字显示,speed 约 40-50ms -- 点击可跳过(立即显示全文) +- 点击/触摸可跳过(立即显示全文) - 每个字符触发打字音效回调 - 显示完毕调用 onDone 回调 @@ -193,7 +194,7 @@ export function getEnding(flags) { - 在最后一段文字打字完成后淡入 - 每个选项延迟入场动画(nth-child animation-delay) - hover 时边框变色 + 微位移 + 阴影扩大 -- 点击触发音效 → 设置 flags → 跳转下一场景 +- 点击触发音效 → 设置 flags → 自动存档 → 跳转下一场景 ### Hash 路由 - URL hash 同步当前场景:`#scene_id` @@ -201,145 +202,158 @@ export function getEnding(flags) { - 监听 hashchange 支持浏览器前进/后退 - 回到标题时清除 hash +### 存档系统(localStorage) +- **自动存档**:每次做出选择后自动保存当前状态到 `localStorage.setItem('novel_autosave', JSON.stringify(state))` +- **手动存档**:支持 3 个存档槽位(`novel_save_1` / `novel_save_2` / `novel_save_3`),每个槽位保存完整 state + 存档时间 + 当前场景标题 +- **TitleScreen 入口**: + - 「新游戏」— 清空状态从头开始 + - 「继续游戏」— 仅当自动存档存在时显示,加载自动存档 + - 「读取存档」— 打开 SaveLoadPanel,显示 3 个手动槽位 +- **SaveLoadPanel**:游戏内可随时打开(菜单按钮/快捷键),支持存档和读档 +- 存档数据结构:`{ state, savedAt, sceneTitle, playTime }` + +### 角色立绘(CharacterPortrait)— 自动适配视频/图片 +- 从 `generated-assets.json` 读取素材信息,根据 `type` 字段渲染: + - `type: "video"` → `