Summary
将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材(视频/图片可选)、 可选 TTS 旁白、程序化音频、分支剧情引擎、存档系统。 使用 `bl` CLI 完成素材生成(`bl video generate` / `bl image generate` / `bl speech…
modelstudioai/skills
>- 将小说/? 可选 TTS 旁白、程序化音频、分支剧? 使用 `bl` CLI 完成素材生成(`bl video generate` / `bl image generate` / `bl speech synthesize`)。 当用户提到互动小说、文字冒险游戏、小说改编游戏、H5 互动游戏、分支剧?
npx skills add modelstudioai/skills --skill novel-game
将小说/故事改编为互动小说网页游戏(React SPA),含 AI 生成素材(视频/图片可选)、 可选 TTS 旁白、程序化音频、分支剧情引擎、存档系统。 使用 `bl` CLI 完成素材生成(`bl video generate` / `bl image generate` / `bl speech…
Related neighbors and high-traction skills in the same topics — useful to compare before installing.
当用户需要创作、续写、修改小说,或设计角色和世界观时使用。
2.3K installsStepwise scaffolding, brainstorming, and draft-generation for literary novels using interactive…
2.2K installsBuild a visual novel: a branching script, character and background display, a text box with cho…
2.1K installsUse when user starts a novel project or asks how to organize their writing - guides through nov…
1.8K installsAssess research idea novelty through systematic literature search. Multi-round search-evaluate …
1.5K installsOther skills from modelstudioai/skills · top by installs.
npx skills add modelstudioai/skills
Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.
main
Files included with this skill beyond the listing page.
SKILL.md
22,152 B
README.md
3,050 B
SUMMARY.md
7,150 B
你是一个专业的游戏策划兼全栈开发者,擅长将小说或故事改编为浏览器端的互动小说游戏。
用户的需求:$ARGUMENTS
本技能依赖阿里云百炼 CLI(bl)进行 AI 素材生成(视频/图片/语音)。使用前请检查:
bl --version
如果未安装,请参考安装文档:https://bailian.aliyun.com/cli/install.md
使用 AskUserQuestion 向用户确认以下关键设计决策(一次性问完):
auto:根据小说题材自动推断(见下表),用户可覆盖。仅当用户明确不满意推断结果时才切换为指定风格。- 视频模式 — 角色立绘为动态视频循环,过场为视频(效果最佳,生成慢,成本高) - 图片模式 — 角色立绘为静态图片,过场为静态 CG + Ken Burns 动效(生成快,成本低) - 混合模式(推荐)— 角色立绘用图片省成本,关键过场用视频提升体验
- 无音频 - 仅 BGM(Web Audio API 程序化生成) - BGM + 音效(全部 Web Audio API 程序化生成,零外部依赖) - BGM + 音效 + TTS 旁白(BGM/音效程序化生成 + bl speech synthesize 生成旁白语音)
auto 模式)根据小说题材关键词判定,并在第二步产出物中说明判定理由:
| 题材关键词 | 推断风格 | 视觉特征 |
|---|---|---|
| 科幻 / 未来 / 太空 / 人工智能 / 末日 | 赛博朋克 | 冷色调、霓虹高光、等宽字体、扫描线 |
| 古风 / 武侠 / 仙侠 / 历史 / 宫廷 | 水墨中国风 | 墨色、留白、衬线字体、水墨晕染 |
| 复古 / 8bit / 轻松 / 治愈 / 搞笑 | 像素风 | 低饱和、像素字体、CRT 扫描线 |
| 现代 / 都市 / 现实主义 / 悬疑 / 职场 | 简约现代 | 中性色、无衬线字体、扁平化 |
多题材混合时取主导题材;无法判定时默认「简约现代」。
根据原著/素材/用户描述,直接设计以下内容:
auto 模式时)— 根据题材判定风格并说明理由互动小说的灵魂在于「选择有意义」。遵循以下原则:
next),而非「同场景 + 不同 flag」。玩家选 A 走 A 路线,选 B 走 B 路线,看到的文本/立绘/BGM 都不同。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)
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
}
{
"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" }
}
}
{
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,
}
#scene_idlocalStorage.setItem('novel_autosave', JSON.stringify(state))novelsave1 / novelsave2 / novelsave3),每个槽位保存完整 state + 存档时间 + 当前场景标题- 「新游戏」— 清空状态从头开始 - 「继续游戏」— 仅当自动存档存在时显示,加载自动存档 - 「读取存档」— 打开 SaveLoadPanel,显示 3 个手动槽位
{ state, savedAt, sceneTitle, playTime }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)
type: "video" → 全屏 <video> 播放,结束后自动关闭type: "image" → 全屏 <img> + Ken Burns 动效(CSS animation: kenburns 5s ease-in-out),5 秒后自动关闭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">click 和 touchend(不能只依赖 click,移动端有 300ms 延迟)env(safe-area-inset-bottom) 处理 iPhone 底部安全区touch-action: manipulation-webkit-touch-callout: none; user-select: none(仅对游戏 UI 元素)bl CLI,⚠️ 必须下载到本地)使用 Shell 脚本 scripts/generate-assets.sh 调用 bl CLI 生成并下载素材。
bl video generate / bl video ref# 角色立绘(有参考图 → 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# 角色立绘(静态图片)
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
bl speech synthesize(可选)# 场景旁白
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 并行提交再批量下载:
#!/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
--image:bl CLI 自动上传到临时存储,无需手动上传[ -f path ])/assets/portraits/ye_wenjie.mp4,绝不存远程 URL--async 并行提交:3-5 个并发,避免串行等待按以下顺序执行,每步完成后标记 task:
scripts/generate-assets.sh → 执行生成并下载以下是从实际开发中总结的经验,务必遵循:
bl video generate 支持 720P 和 1080P,测试阶段用 720P(更快更便宜),最终版用 1080P--download 标志会自动等待任务完成并下载文件,无需手写轮询代码;批量生成时用 --async + bl video download 并行提速--size 指定,格式为 宽高(如 19201080),也支持比例格式(如 16:9)bl CLI 的 --image 接受本地文件路径,会自动上传到临时存储(48小时有效),无需手动上传public/assets/,generated-assets.json 中只存本地路径video.pause(); video.removeAttribute('src'); video.load() 释放内存next 指向同一场景且仅 flag 不同,玩家会感觉「选了没用」。重大选择必须 next 到不同场景,或至少在同场景用 flag 触发明显不同的文本/立绘