Post

Twine + SugarCube 互动小说入门教程

Twine + SugarCube 互动小说入门教程

面向对象

适用项目:魔法档案调查、校园悬疑、恋爱分支、文字冒险、档案收集类互动小说。


1. Twine 和 SugarCube 是什么

1.1 Twine

Twine 是一个制作互动小说的工具。

普通小说是线性的:

1
第一章 → 第二章 → 第三章 → 结尾

互动小说是分支的:

1
2
3
猫头鹰送来红信
├── 拆开红信 → 发现课程表
└── 藏起红信 → 夜晚抵达城堡

Twine 的作用是:让你把故事拆成一个个小段落,再用选项把它们连接起来。

官网:https://twinery.org/

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 快速调试技巧

如果某个选项不工作,检查:

  1. 节点名字是否拼对? —— [[去城堡|成堡]][[去城堡|城堡]] 是两个不同的节点
  2. 变量名是否写对? —— $clue += 1$clue += 2 不同;$clue$clue2 也不同
  3. 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 浏览器的开发者工具终极检查

如果以上都不能解决问题,在故事页面按 F12Console,输入:

1
2
// 查看所有变量
state.variables

这会列出当前游戏中所有变量的值和类型,是排查问题的最强工具。


17. 发布到网页

做好故事后,你可以把它发布到网上,让别人也能玩到。

17.1 导出 HTML 文件

在 Twine 编辑器中:

  1. 点击 Build → Publish to File(生成 → 发布到文件)
  2. 保存得到一个 .html 文件

这个 HTML 文件是自包含的——打开就能玩,不需要安装任何东西。

17.2 发布到 GitHub Pages(免费)

如果你有 GitHub 账号,可以免费托管互动小说:

  1. 在 GitHub 上新建一个仓库,名字叫 你的用户名.github.io
  2. 把导出的 .html 文件改名为 index.html
  3. 推送到 GitHub
  4. 访问 https://你的用户名.github.io/故事名/ 即可

17.3 发布到 Netlify(免费)

  1. 打开 https://app.netlify.com/
  2. .html 文件拖拽到浏览器中
  3. 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. 推荐学习链接

官方资料

入门教程


21. 建议练习路线

不要一开始就写长篇。先做一个 10 分钟能玩完的小版本。

最小结构:

1
Start → 课程表 → 药剂标签 → 第一个伸手的人 → 天文塔 → 结局

最小系统: 线索、信任、风险、档案夹、三个结局。

写作时可以用这个表格设计每一章:

字段内容
章节名地窖里的错标药剂
地点魔药课教室
关键道具银色沉淀药剂
玩家发现标签被调换
可选行动1. 刮下标签 → 线索 +1;2. 偷走药剂 → 风险 +2;3. 找人询问 → 信任 +1
通向天文塔

你真正要学的不是代码,而是如何设计选择、如何设计代价、如何安排线索、如何让结局回应玩家一路上的决定。SugarCube 只是把这些设计写进 Twine 的工具。

This post is licensed under CC BY 4.0 by the author.