Twine + SugarCube 互动小说入门教程
面向对象
适用项目:魔法档案调查、校园悬疑、恋爱分支、文字冒险、档案收集类互动小说。
1. Twine 和 SugarCube 是什么
1.1 Twine
Twine 是一个制作互动小说的工具。
普通小说是线性的:
1
第一章 → 第二章 → 第三章 → 结尾
互动小说是分支的:
1
2
3
猫头鹰送来红信
├── 拆开红信 → 发现课程表
└── 藏起红信 → 夜晚抵达城堡
Twine 的作用是:让你把故事拆成一个个小段落,再用选项把它们连接起来。
1.2 SugarCube
SugarCube 是 Twine 的一种”故事格式”。
你可以这样理解:
1
2
Twine = 写互动小说的软件
SugarCube = 让互动小说更像游戏的规则系统
SugarCube 帮你实现:线索值、信任值、风险值、档案夹、条件分支、多结局、存档读档、自定义界面。
官方文档:https://www.motoslave.net/sugarcube/2/docs/
2. 安装与准备
打开 https://twinery.org/,可以使用网页版,也可以下载桌面版。
点击 + Story(或中文界面的 新建故事),故事名可以写”霍格沃兹失物档案”。
进入故事编辑页面后,找到 Story → Details → Story Format(故事 → 故事详情 → 故事格式),把格式改为 SugarCube 2。如果格式不是 SugarCube,后面的语法无法正常运行。
3. 第一个 Passage
打开默认的第一个节点(一般叫 Start),写入:
午夜刚过,猫头鹰叩响了你的窗户。
它带来一封没有署名的红信。
信纸自己展开,上面写着:
''回到霍格沃兹。''
你要怎么做?
[[拆开红信|课程表]]
[[暂时藏起红信|抵达城堡]]
这就是一个最简单的互动小说开头。
4. Passage:故事节点
在 Twine 里,每一个故事片段叫做 Passage。你可以把它理解成一个场景、一个页面、一个章节片段。
例如:
1
Start → 课程表 → 抵达城堡 → 药剂标签 → 天文塔 → 结局
每个 Passage 里写一段剧情,然后用链接跳到别的 Passage。
5. 链接语法
[[读者看到的选项文字|跳转到的节点名字]]
例子:
[[拆开红信|课程表]]
读者看到”拆开红信”,点击后进入”课程表”这个 Passage。
6. SugarCube 变量:让故事记住选择
互动小说最重要的能力是:故事会记住读者做过什么。
6.1 新建 StoryInit
新建一个特殊 Passage,名字必须是 StoryInit。写入:
<<set $clue = 0>>
<<set $trust = 0>>
<<set $risk = 0>>
故事开始时:线索 = 0,信任 = 0,风险 = 0。
6.2 让选择改变变量
回到 Start,把选项改成:
[[拆开红信|课程表][$clue += 1]]
[[暂时藏起红信|抵达城堡][$risk += 1]]
$clue += 1 的意思是”线索加 1”,$risk += 1 是”风险加 1”。
所以 [[拆开红信|课程表][$clue += 1]] 的完整意思是:玩家点击”拆开红信”,跳转到”课程表”,同时线索加 1。
7. 状态栏
新建一个特殊 Passage,名字叫 StoryCaption。写入:
''案件状态''
线索:$clue
信任:$trust
禁忌风险:$risk
这样玩家在侧边栏就能实时看到状态变化。
8. 档案夹系统
8.1 初始化
把 StoryInit 改成:
<<set $clue = 0>>
<<set $trust = 0>>
<<set $risk = 0>>
<<set $inventory = []>>
$inventory = [] 创建一个空档案夹。
8.2 获得档案
[[拆开红信|课程表][$clue += 1; $inventory.pushUnique("红信")]]
线索加 1,同时把”红信”放进档案夹。
8.3 显示档案夹
把 StoryCaption 改成:
''案件状态''
线索:$clue
信任:$trust
禁忌风险:$risk
''档案夹''
<<if $inventory.length == 0>>
暂无档案
<<else>>
<<for _item range $inventory>>
* _item
<</for>>
<</if>>
9. 条件分支
SugarCube 可以根据变量显示不同文本:
<<if $clue >= 3>>
你终于看懂了课程表背面的隐形墨水。
<<else>>
这张纸暂时没有更多线索。
<</if>>
10. 多结局设计
新建一个 Passage 叫”结局”,写入:
你站在有求必应屋门前。
所有档案在风中翻开。
<<if $clue >= 4 and $trust >= 2 and $risk <= 3>>
门打开了。
你找回了那段被封存的记忆。
''真相结局''
<<elseif $risk >= 4>>
银色雾气从门缝里涌出。
你听见有人喊你的名字,可你已经想不起那是谁。
''记忆封存结局''
<<else>>
门只打开了一条缝。
你知道自己还缺少关键线索。
''未完成结局''
<</if>>
[[重新开始|Start][$clue = 0; $trust = 0; $risk = 0; $inventory = []]]
结局不是由最后一个选择决定,而是由玩家一路积累的状态决定。
11. 加入图片
可以在 Passage 里写 HTML 图片:
1
<img src="https://example.com/howler.jpg" style="width:300px;">
初学阶段建议:先完成文字、分支、变量和结局,再统一处理图片。
12. 导入和导出
导入:Twine 支持 .twee 和 .html 文件。详见 官方说明。
导出:Build → Publish to File(生成 → 发布到文件),得到一个 HTML 文件,可以发给别人或部署到网页。
13. 在 Twine 编辑器中操作
前面的教程用了大量代码来讲解。现在回到 Twine 编辑器本身,了解如何可视化管理你的故事。
13.1 添加新 Passage
在编辑器中,点击 + Passage 按钮(或双击画布空白处),就会创建一个新的空白节点。
你可以给节点取任何名字,例如:课程表、药剂标签、天文塔。
13.2 连接 Passage
把鼠标悬停在一个节点上,会看到一个 拖拽手柄(一个小圆点)。
按住它拖到另一个节点上,就在两个节点之间画出了一条箭头——这就是故事流向。
在 Twine 中制作链接有两种等价方式:
- 在代码里写
[[选项|目标节点]]→ 链接会自动出现 - 拖拽连接两个节点 → Twine 会自动在源节点末尾添加链接代码
两种方式可以混用,结果一样。
13.3 删除 Passage
右键点击节点 → Delete Passage(删除节点)。
删除前确保没有其他节点链接到它,否则故事会断掉。
13.4 测试故事
任何时候点击编辑器底部的 Play(▶ 按钮),就会在浏览器中打开你的故事,从头开始玩一遍。
这是测试分支是否连通、变量是否正确的最快方式。
14. 预览和测试故事
14.1 故事测试清单
写完一个版本后,按这个清单逐一检查:
1
2
3
4
5
6
7
☐ 每个选项真的跳到了正确节点?
☐ 所有变量在开始时归零?(StoryInit 是否已设置?)
☐ 状态栏显示了所有变量?
☐ 档案夹能正常加入和显示物品?
☐ 每个条件分支都能走到?($clue >= 3 时发生了什么?$risk >= 4 呢?)
☐ 每个结局都能到达?
☐ 重新开始按钮重置了所有变量?
14.2 快速调试技巧
如果某个选项不工作,检查:
- 节点名字是否拼对? ——
[[去城堡|成堡]]和[[去城堡|城堡]]是两个不同的节点 - 变量名是否写对? ——
$clue += 1和$clue += 2不同;$clue和$clue2也不同 - StoryInit 是否真的存在? —— 名字必须一字不差
14.3 在浏览器中调试
在故事页面按 F12 打开开发者工具,切换到 Console(控制台) 标签。
你可以直接输入命令查看变量:
1
2
3
4
5
6
7
8
// 查看当前线索值
state.variables.clue
// 查看档案夹
state.variables.inventory
// 手动设置变量(用于测试某个分支)
state.variables.clue = 3
15. SugarCube 文字样式
在 Passage 中,你可以用一些简单标记让文字更有表现力。
15.1 基础样式
// 斜体(用两个单引号包住)
''这是斜体文字''
// 粗体(用两个星号包住)
**这是粗体文字**
// 删除线(用两个浪纹线包住)
~~这是删除文字~~
// 等宽代码字体
""这是打字机字体""
15.2 标题和分隔线
// 大标题
!!! 第一章:猫头鹰来信
// 中标题
!! 城堡深处
// 小标题
! 地窖
// 水平分隔线
---
注意:在 Twine/SugarCube 中,! 开头的行会变成标题,--- 会变成一条横线。这和 Markdown 不太一样,在 Twine 里需要用 --- 而不是 Markdown 语法。
15.3 文字颜色
用 HTML 标签可以改变颜色:
1
2
3
<span style="color:red;">危险!</span>
<span style="color:gold;">金色飞贼</span>
<span style="color:silver;">银色粉末</span>
15.4 换行
在 Passage 中,直接按 Enter 换行即可。SugarCube 会保持你的换行格式。
如果想在同一个段落内换行(不产生新段落),用:
<br>
16. 常见错误排查
16.1 选项没有出现
1
2
原因:节点名字写错了
解决:检查 [[选项|目标节点]] 中的目标节点名字是否完全匹配
16.2 变量始终是 0
1
2
原因:没有设置 StoryInit
解决:新建一个名为 StoryInit 的节点,写上 <<set $clue = 0>> 等初始化语句
16.3 状态栏不显示
1
2
原因:没有创建 StoryCaption
解决:新建一个名为 StoryCaption 的节点,写上要显示的变量
16.4 条件分支没有生效
1
2
3
4
5
6
例如写了 <<if $clue >= 3>> 但始终不显示该段。
原因:
1. $clue 的值确实不到 3(在 StoryInit 里检查初始值)
2. 变量名写错了(比如写了 <<if $clue >= 3>> 但实际使用的是 $clue2)
3. 忘记写 <</if>> 来结束条件块
16.5 档案夹不显示物品
1
2
3
4
5
原因:
1. StoryCaption 中的循环代码写错了
2. pushUnique 时物品名字写错了
解决:检查 <<for _item range $inventory>> 是否正确
16.6 重新开始后变量没重置
1
2
3
4
原因:重新开始的链接没有重置变量
正确写法:
[[重新开始|Start][$clue = 0; $trust = 0; $risk = 0; $inventory = []]]
16.7 浏览器的开发者工具终极检查
如果以上都不能解决问题,在故事页面按 F12 → Console,输入:
1
2
// 查看所有变量
state.variables
这会列出当前游戏中所有变量的值和类型,是排查问题的最强工具。
17. 发布到网页
做好故事后,你可以把它发布到网上,让别人也能玩到。
17.1 导出 HTML 文件
在 Twine 编辑器中:
- 点击 Build → Publish to File(生成 → 发布到文件)
- 保存得到一个
.html文件
这个 HTML 文件是自包含的——打开就能玩,不需要安装任何东西。
17.2 发布到 GitHub Pages(免费)
如果你有 GitHub 账号,可以免费托管互动小说:
- 在 GitHub 上新建一个仓库,名字叫
你的用户名.github.io - 把导出的
.html文件改名为index.html - 推送到 GitHub
- 访问
https://你的用户名.github.io/故事名/即可
17.3 发布到 Netlify(免费)
- 打开 https://app.netlify.com/
- 把
.html文件拖拽到浏览器中 - Netlify 会自动生成一个可分享的链接
17.4 直接发送给别人
导出的 .html 文件可以直接发微信、钉钉、邮件等。别人收到后双击就能玩,不需要安装任何软件。
18. 完整练习模板
你可以把下面内容复制到 .twee 文件中,或在 Twine 里逐个建立 Passage。
:: StoryInit
<<set $clue = 0>>
<<set $trust = 0>>
<<set $risk = 0>>
<<set $inventory = []>>
:: StoryCaption
''案件状态''
线索:$clue
信任:$trust
禁忌风险:$risk
''档案夹''
<<if $inventory.length == 0>>
暂无档案
<<else>>
<<for _item range $inventory>>
* _item
<</for>>
<</if>>
:: Start
午夜刚过,猫头鹰叩响了你的窗户。
它带来一封没有署名的红信。
你要怎么做?
[[拆开红信|课程表][$clue += 1; $inventory.pushUnique("红信")]]
[[暂时藏起红信|抵达城堡][$risk += 1; $inventory.pushUnique("红信")]]
:: 抵达城堡
你抵达霍格沃兹。
移动楼梯转向了相反方向。
[[去图书馆|课程表][$clue += 1]]
[[去地窖|药剂标签][$risk += 1]]
:: 课程表
你发现一张异常课程表。
背面写着:
//时间不是回到过去,时间只是把债务延期。//
[[记录课程表|唱唱反调][$clue += 1; $inventory.pushUnique("异常课程表")]]
[[拿给熟人看|第一个伸手的人][$trust += 1; $inventory.pushUnique("异常课程表")]]
:: 唱唱反调
你在旧杂志里找到一页残缺报道。
报道说:城堡会吞下不愿被记录的历史。
[[去天文塔|天文塔][$clue += 1]]
:: 第一个伸手的人
他看完课程表,沉默了很久。
"你不该回来。"他说。
[[相信他|天文塔][$trust += 2]]
[[保持怀疑|天文塔][$clue += 1]]
:: 药剂标签
地窖里有一瓶错标药剂。
标签写着镇定剂,瓶底却沉着银色粉末。
[[刮下标签|天文塔][$clue += 1; $inventory.pushUnique("错标药剂标签")]]
[[偷走药剂|天文塔][$risk += 2; $inventory.pushUnique("未登记药剂")]]
:: 天文塔
你来到天文塔。
风把所有档案吹开。
红信、课程表、杂志缺页、药剂标签,正在拼成一个名字。
[[打开有求必应屋|结局]]
:: 结局
你站在有求必应屋门前。
<<if $clue >= 4 and $trust >= 2 and $risk <= 3>>
门打开了。
你找回了那段被封存的记忆。
''真相结局''
<<elseif $risk >= 4>>
银色雾气吞没了你的名字。
''记忆封存结局''
<<else>>
门只打开了一条缝。
你还缺少关键线索。
''未完成结局''
<</if>>
[[重新开始|Start][$clue = 0; $trust = 0; $risk = 0; $inventory = []]]
19. 常用语法速查
| 用途 | 语法 | 示例 |
|---|---|---|
| 跳转链接 | [[文字\|节点]] | [[去图书馆\|课程表]] |
| 设置变量 | <<set $x = 值>> | <<set $clue = 0>> |
| 增加变量 | <<set $x += n>> | <<set $clue += 1>> |
| 选项增加值 | [[文字\|节点][$x += n]] | [[记录线索\|下一页][$clue += 1]] |
| 条件判断 | <<if>>…<<else>>…<</if>> | <<if $clue >= 3>>…<<else>>…<</if>> |
| 档案加入物品 | <<run $inventory.pushUnique("物品")>> | 同上,也可写在选项里 |
| 循环显示档案 | <<for _item range $inventory>> | 循环输出所有物品 |
20. 推荐学习链接
官方资料
- Twine 官网:https://twinery.org/
- Twine 官方参考文档:https://twinery.org/reference/en/
- Twine Cookbook:https://twinery.org/cookbook/
- SugarCube 官方文档:https://www.motoslave.net/sugarcube/2/docs/
- SugarCube 变量教程:https://twinery.org/cookbook/settingandshowing/sugarcube/sugarcube_settingandshowing.html
- SugarCube 数组/背包教程:https://twinery.org/cookbook/arrays/sugarcube/sugarcube_arrays.html
入门教程
- A Quick Twine Tutorial:https://catn.decontextualize.com/twine/
- OpenSource.com: How to use Twine and SugarCube:https://opensource.com/article/18/2/twine-gaming
21. 建议练习路线
不要一开始就写长篇。先做一个 10 分钟能玩完的小版本。
最小结构:
1
Start → 课程表 → 药剂标签 → 第一个伸手的人 → 天文塔 → 结局
最小系统: 线索、信任、风险、档案夹、三个结局。
写作时可以用这个表格设计每一章:
| 字段 | 内容 |
|---|---|
| 章节名 | 地窖里的错标药剂 |
| 地点 | 魔药课教室 |
| 关键道具 | 银色沉淀药剂 |
| 玩家发现 | 标签被调换 |
| 可选行动 | 1. 刮下标签 → 线索 +1;2. 偷走药剂 → 风险 +2;3. 找人询问 → 信任 +1 |
| 通向 | 天文塔 |
你真正要学的不是代码,而是如何设计选择、如何设计代价、如何安排线索、如何让结局回应玩家一路上的决定。SugarCube 只是把这些设计写进 Twine 的工具。