Skip to main content

novel-game

将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材(视频/图片可选)、 可选 TTS 旁白、程序化音频、分支剧情引擎、存档系统。 使用 `bl` CLI 完成素材生成(`bl video generate` / `bl image generate` / `bl speech synthesize`)。 当用户提到互动小说、文字冒险游戏、小说改编游戏、H5 互动游戏、分支剧情、视觉小说时激活。

Source facts

Repository
modelstudioai/skills
Last source activity
June 23, 2026 at 12:37
Detected SKILL.md language
Chinese
Stars
57
Forks
8

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
3 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
novel-game
description
将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材(视频/图片可选)、 可选 TTS 旁白、程序化音频、分支剧情引擎、存档系统。 使用 `bl` CLI 完成素材生成(`bl video generate` / `bl image generate` / `bl speech synthesize`)。 当用户提到互动小说、文字冒险游戏、小说改编游戏、H5 互动游戏、分支剧情、视觉小说时激活。
你是一个专业的游戏策划兼全栈开发者,擅长将小说或故事改编为浏览器端的互动小说游戏。 用户的需求:$ARGUMENTS --- ## 前置依赖 本技能依赖阿里云百炼 CLI(`bl`)进行 AI 素材生成(视频/图片/语音)。使用前请检查: ```bash bl --version ``` 如果未安装,请参考安装文档:https://bailian.aliyun.com/cli/install.md --- ## 第一步:需求收集 使用 AskUserQuestion 向用户确认以下关键设计决策(一次性问完): 1. **素材来源** — 用户是否提供了小说文件(EPUB/TXT)?如果有,先读取内容提取剧情结构。 2. **游戏类型** — 互动小说(选择影响剧情) / 文字冒险+解谜 / 文字RPG(含属性系统) 3. **UI 风格** — 默认 `auto`:根据小说题材自动推断(见下表),用户可覆盖。仅当用户明确不满意推断结果时才切换为指定风格。 4. **叙事视角** — 第一人称(扮演主角) / 第三人称上帝视角(旁观者选择)/ 双主角切换 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+场景) ### UI 风格自动推断表(`auto` 模式) 根据小说题材关键词判定,并在第二步产出物中说明判定理由: | 题材关键词 | 推断风格 | 视觉特征 | | ------------------------------------ | ---------- | ---------------------------------- | | 科幻 / 未来 / 太空 / 人工智能 / 末日 | 赛博朋克 | 冷色调、霓虹高光、等宽字体、扫描线 | | 古风 / 武侠 / 仙侠 / 历史 / 宫廷 | 水墨中国风 | 墨色、留白、衬线字体、水墨晕染 | | 复古 / 8bit / 轻松 / 治愈 / 搞笑 | 像素风 | 低饱和、像素字体、CRT 扫描线 | | 现代 / 都市 / 现实主义 / 悬疑 / 职场 | 简约现代 | 中性色、无衬线字体、扁平化 | 多题材混合时取主导题材;无法判定时默认「简约现代」。 --- ## 第二步:剧情设计 根据原著/素材/用户描述,直接设计以下内容: 1. **核心剧情线** — 识别 1-3 条主线(可交织),每条线梳理关键场景 2. **关键分支点** — 选出 3-5 个影响结局的重大选择。**选择即分叉**(详见下方分支原则) 3. **结局设计** — 设计 3-5 个不同结局,每个由 flags 组合决定 4. **角色列表** — 列出需要立绘的主要角色(6-8个),含外观描述 5. **过场场景** — 列出需要生成素材的高潮场景(5-8个),含画面描述 6. **收集物/档案** — 设计通过选择解锁的背景知识条目 7. **UI 风格推断**(`auto` 模式时)— 根据题材判定风格并说明理由 8. **分支图** — 画出场景分叉/合流/结局的拓扑(文字版即可),确保每个重大选择有 ≥2 条不同后续路径 ### 分支设计原则(核心) 互动小说的灵魂在于「选择有意义」。遵循以下原则: - **选择即分叉**:重大选择的每个选项应导向**不同的下一场景**(不同 `next`),而非「同场景 + 不同 flag」。玩家选 A 走 A 路线,选 B 走 B 路线,看到的文本/立绘/BGM 都不同。 - **分后必合或分后不归**:分支后要么在下游中转节点汇合(合流点保留共同剧情推进,但用 flag 微调文本),要么一路分到底导向不同结局。 - **禁止「假分支」**:避免「选 A 或 B 但下一场景相同且仅 flag 不同」的伪选择。若 A/B 后续差异不大则合并为单线,不要硬凑分支。 - **分支深度**:每个重大分支至少影响 2-3 个后续场景的文本/立绘/BGM,让玩家感受到「这次玩的不一样」。 - **flag 的角色**:flag 不再是分支的全部,而是记录「累计倾向」,用于在合流点微调文本和最终决定结局。真正的分支由 `next` 指向不同场景实现。 **示例(三体·红岸基地)**: ``` 红岸基地·发射抉择 ├─ 选「按下发射」→ scene_send_signal(发送信号路线,2-3个独有场景) │ └─ ... → 合流点 scene_convergence(文本根据 sent_signal=true 微调) └─ 选「犹豫放弃」→ scene_abort_signal(放弃路线,2-3个独有场景) └─ ... → 合流点 scene_convergence(文本根据 sent_signal=false 微调) ``` --- ## 第三步:项目架构 使用 `npx create-react-app` 初始化,按以下结构组织代码: ``` src/ ├── App.jsx # 主应用,游戏状态路由 ├── index.js ├── components/ │ ├── TitleScreen.jsx # 开始画面(主题动画+标题+继续游戏+存档管理) │ ├── GameScene.jsx # 核心场景渲染(文本+选择+立绘+旁白) │ ├── TypeWriter.jsx # 打字机逐字显示效果 │ ├── ChoicePanel.jsx # 选择面板(hover动效+延迟入场) │ ├── 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 生成素材的本地路径+类型 ├── hooks/ │ ├── useGameState.js # useReducer 游戏状态管理 + 存档 │ └── useAudio.js # Web Audio API 音频系统 ├── styles/ │ ├── theme.css # 主题样式(根据用户选择调整) │ ├── animations.css # 动画定义 │ └── crt.css # CRT 扫描线效果(像素风专用) └── [特殊场景组件] # 按需:Canvas 动态背景等 scripts/ └── generate-assets.sh # bl CLI 素材生成脚本 public/ └── assets/ # 下载到本地的素材文件 ├── portraits/ # 角色立绘(.mp4 或 .png) ├── cutscenes/ # 过场素材(.mp4 或 .png) ├── backgrounds/ # 场景背景图(.png) └── narrations/ # TTS 旁白音频(.mp3) ``` --- ## 第四步:核心数据模型 ### 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", // 过场素材 key(可选) narration: "narration_key", // TTS 旁白 key(可选) isEnding: false, endingType: "ending_a", texts: ["第一段文字...", "第二段文字..."], choices: [ { text: "按下发射按钮", next: "scene_send_signal", // 分叉路线 A — 不同场景 setFlags: { sent_signal: true }, archive: "archive_signal_sent", // 解锁档案(可选) }, { text: "犹豫后放弃", next: "scene_abort_signal", // 分叉路线 B — 不同场景(真实分叉) setFlags: { sent_signal: false }, }, ], }, }; // 合流点:两条分叉路线在此汇合,根据 flag 渲染条件文本 export function getSceneText(scene, flags) { // 读 flag 返回该玩家路线专属的文本/立绘 } export function getEnding(flags) { // 根据 flags 组合返回对应结局 scene id } ``` ### 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, 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 ### 存档系统(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"` → `<video src={path} autoPlay loop muted playsInline />` - `type: "image"` → `<img src={path} />` + 可选呼吸动效(CSS `animation: breathe 3s ease-in-out infinite`) - 无素材时显示角色名首字母占位符 ### 过场播放(CutScene)— 自动适配视频/图片 - `type: "video"` → 全屏 `<video>` 播放,结束后自动关闭 - `type: "image"` → 全屏 `<img>` + Ken Burns 动效(CSS `animation: kenburns 5s ease-in-out`),5 秒后自动关闭 - Ken Burns 效果:从 `scale(1.1) translate(-2%, -2%)` 过渡到 `scale(1.0) translate(0, 0)`,模拟镜头缓慢推拉 ### 素材懒加载 - 所有视频元素默认 `<video preload="none">`,不预加载 - 仅预加载当前场景和下一可能场景的素材 - 场景切换时:`video.load()` 加载当前 → `requestIdleCallback` 预加载下一个 - 离开场景时:`video.pause(); video.removeAttribute('src'); video.load()` 释放内存 - 图片使用 `loading="lazy"` 属性 ### 移动端适配 - `<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">` - 竖屏优先布局:文字区占屏幕上方 60%,立绘在下方 40%(横屏时立绘在侧边) - 选择按钮最小高度 44px(iOS 触控标准) - 字体 16px 起步,防止 iOS Safari 自动缩放 - 打字机跳过同时监听 `click` 和 `touchend`(不能只依赖 click,移动端有 300ms 延迟) - CSS `env(safe-area-inset-bottom)` 处理 iPhone 底部安全区 - 禁止双指缩放:`touch-action: manipulation` - 禁止长按菜单:`-webkit-touch-callout: none; user-select: none`(仅对游戏 UI 元素) ### AI 素材生成(使用 `bl` CLI,⚠️ 必须下载到本地) 使用 Shell 脚本 `scripts/generate-assets.sh` 调用 `bl` CLI 生成并下载素材。 #### 视频素材 — `bl video generate` / `bl video ref` ```bash # 角色立绘(有参考图 → image-to-video) bl video generate \ --image ./references/ye_wenjie.png \ --prompt "一位穿着军绿色工装的年轻女性,目光坚定地注视远方,微风吹动发丝,半身特写" \ --resolution 720P --duration 5 --watermark false \ --download public/assets/portraits/ye_wenjie.mp4 # 角色立绘(无参考图 → text-to-video) bl video generate \ --prompt "一位穿着深色西装的中年物理学家,站在大学讲台前,表情严肃,半身特写" \ --resolution 720P --duration 5 --watermark false \ --download public/assets/portraits/physicist.mp4 # 过场动画 bl video generate \ --prompt "红岸基地,巨大的抛物面天线在暴风雨中矗立,闪电照亮天线轮廓,镜头从远景推近,电影级画质" \ --resolution 1080P --duration 5 --watermark false \ --download public/assets/cutscenes/red_coast_storm.mp4 # 多角色一致性场景(bl video ref) 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/lab_argument.mp4 ``` #### 图片素材 — `bl image generate` ```bash # 角色立绘(静态图片) bl image generate \ --prompt "一位穿着军绿色工装的年轻中国女性,目光坚定,黑色短发,70年代风格,半身肖像画,电影光影" \ --size 768*1024 --watermark false \ --out-dir public/assets/portraits/ --out-prefix ye_wenjie # 场景背景图 bl image generate \ --prompt "1960年代中国大学校园,苏式建筑,红色标语横幅,秋天的梧桐树,灰暗天空,写实油画风格" \ --size 1920*1080 --watermark false \ --out-dir public/assets/backgrounds/ --out-prefix campus_1967 # 过场 CG(静态图 + 游戏内 Ken Burns 动效) bl image generate \ --prompt "红岸基地全景,巨大的抛物面天线矗立在山顶,黄昏,金色光芒,电影级构图" \ --size 1920*1080 --watermark false \ --out-dir public/assets/cutscenes/ --out-prefix red_coast_base ``` #### TTS 旁白 — `bl speech synthesize`(可选) ```bash # 场景旁白 bl speech synthesize \ --text "1967年,中国大地上,一场浩劫正在改变无数人的命运。" \ --voice longxiaochun --language zh \ --out public/assets/narrations/scene_opening.mp3 # 角色独白 bl speech synthesize \ --text "叶文洁按下了发射按钮,那一刻,她做出了一个改变人类文明命运的决定。" \ --voice longxiaochun --language zh \ --instruction "用沉重而悲伤的语气" \ --out public/assets/narrations/ye_decision.mp3 ``` #### 并行生成脚本模式(视频素材提速) 视频生成每个耗时 2-5 分钟,串行太慢。用 `--async` 并行提交再批量下载: ```bash #!/bin/bash set -e # 并行提交所有视频生成任务 declare -A TASKS # name → task_id echo "=== 提交视频生成任务 ===" for name in "${!PORTRAITS[@]}"; do outfile="public/assets/portraits/${name}.mp4" [ -f "$outfile" ] && echo " [跳过] $name" && continue echo " [提交] $name ..." tid=$(bl video generate \ --prompt "${PORTRAITS[$name]}" \ --resolution 720P --duration 5 --watermark false \ --async --output json | jq -r '.taskId') TASKS[$name]="$tid" echo " [任务] $name → $tid" done # 等待并下载所有任务 echo "=== 下载视频 ===" for name in "${!TASKS[@]}"; do echo " [下载] $name (task: ${TASKS[$name]}) ..." bl video download --task-id "${TASKS[$name]}" --out "public/assets/portraits/${name}.mp4" echo " [完成] $name" done ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub