在 Divooka 中使用 Novella
Divooka.Novella 包将 Novella 的每个概念都做成了节点,这些节点可以在全部四种图中组合使用(见图上下文):
| 图 | 它如何使用 Novella |
|---|---|
| Routine | 一个过程:创建游戏、配置游戏、加载其内容并运行它。 |
| Events | 以类形式编写的游戏:图的基类型为 Novella Application,图实现游戏的回调。 |
| Function | 以数据形式描述游戏:序列、条件、效果、标签、场景、动画、界面、样式、故事、Game Definition。 |
| Flows | 调用这样的函数并预览其返回值。 |
过程式图是基础:游戏之所以能运行,是因为有一个图创建并启动了它。函数式图是编写和预览故事的便捷方式,其结果交给过程式图来真正运行。哪些发行版包含此包,见安装。
Novella 工具箱
所有节点都在一个名为 Novella 的工具箱中,每种类型一个类别。下文中的节点名称就是节点面板中显示的名称。
Novella Game
游戏本身,用于过程式图。除两个构造节点外,每个节点都以游戏作为接收者(在 Routine 图中通过连线传入,在 Events 图中则是隐式的)。
| 节点 | 作用 |
|---|---|
| Make Novella Game | 一个具有标题、宽度和高度的游戏。 |
| Make Novella Game From Definition | 由函数式图构建的 Game Definition 创建的游戏。 |
| Set Resolution、Set Title | 所有内容据以布局的分辨率,以及标题。 |
| Set Config、Set Theme | 一项配置或主题设置,写法与脚本中相同;它优先于脚本自身对同一键的 define。 |
| Add Library Assets | 嵌入在本文档库中的文件。库输入由文档提供,无需连线。 |
| Add Asset Folder、Add Asset | 磁盘上的一个文件夹,或来自字节数据的单个文件。 |
| Load Script、Load Script File、Load Scripts From Assets | 脚本文本、一个脚本文件,或资源中的每个 .novella 文件(可选限定在某个文件夹下)。 |
| Add Story | 由函数式节点构建的故事。 |
| Add Scene | 来自 Scenes 节点的一个 Scene,以其名称作为标签添加。 |
| Define Character、Define Image、Set Default | 无需脚本的定义。 |
| Register Command | 将一个 run 命令名称绑定到一个没有输入也没有输出的函数(见函数引用)。 |
| Register Command Function | 将一个 run 命令名称绑定到一个函数,该函数以文本形式接受命令的实参并以文本应答,故事将应答读作 _return:数字、true 或 false,否则为文本。 |
| Register Expression Function | 将一个可由条件、$ 行和 [interpolation] 调用的名称(例如 [title_for(trust)])绑定到一个函数,该函数以文本形式接受实参并以文本应答,应答的解读方式相同。 |
| Validate | 静态检查(lint):每个问题一行。 |
| Run | 在窗口中打开游戏,并在玩家退出时返回。 |
同一类别中还有实时脚本节点,它们在实时脚本运行时会立即播放(见 Events 图):Say、Narrate、Show、Scene、Hide、With、Pause、Play、Stop、Menu(返回所选项的索引)、Input(返回输入的文本)、Call Label、Notify、Achieve、Set Variable、Get Variable、Get Number 和 Random Integer。Random Integer 从故事自身的生成器中抽取,因此回滚后再次选择会抽到相同的数。另有两个节点服务于 Command 事件:Set Result 应答正在处理的命令,Translate Text 让文本经过故事的字符串翻译。
Story Writing
构建 Sequence 的纯节点:每个节点接受一个序列,并返回一个多了一条语句的新序列,因此一条链从上到下读起来就像脚本。
| 节点 | 语句 |
|---|---|
| Start Sequence | 一个可在其上构建的空序列。 |
| Say、Narrate | 一句对话(角色 id、文本、可选的图像属性),或旁白。 |
| Show、Scene、Hide、With | 舞台变化,带有位置(left、right、已定义的变换)和转场(dissolve、fade(1.0))。 |
| Pause | 等待一次点击,或若干秒。 |
| Play Music、Play Sound、Play、Stop | 在音乐通道、音效通道或任意通道上的音频。 |
| Menu | 一句提示文字和两到四个 Choice 值,每个值都携带其后续的序列。 |
| If | 一个来自 Conditions 节点的 Condition、条件成立时的序列,以及可选的条件不成立时的序列。 |
| Apply Effect | 一个来自 Effects 节点的 Effect,例如 Add To Variable:故事的变量在此处发生变化。 |
| Jump、Call、Return | 标签之间的流程。 |
| Input、Notify、Achieve、Movie、Voice、Window | 对应的语句。 |
| Run Command | 一个 run 命令,带有以逗号分隔的实参表达式。 |
| Script | 以脚本形式编写的任意语句,用于其他节点未涵盖的内容。 |
| Concatenate | 一个序列后接另一个序列。 |
| Sequence To Script | 将序列转为脚本文本,用于查看一条链写出了什么。 |
上面的节点涵盖了大多数故事所需的内容。下面这些节点则逐个选项地表达脚本能表达的其余内容:
| 节点 | 语句 |
|---|---|
| Say Line | 一句台词,带有持续的属性、仅对本句有效的属性、台词出现时播放的转场,以及用于翻译的 id。 |
| Say Briefly、Say As、Extend、Centered、NVL Clear | 一句属性仅在其显示期间有效的台词;一句来自一次性说话者的台词;接续上一句台词;屏幕正中的文本;一个新的 NVL 页面。 |
| Show With Options | 脚本 show 的每个子句:at、with、behind、zorder、as 和 layer,以及一个来自 Animation 节点、作为其自身时间线的 Animation。 |
| Scene With Options、Hide With Options | 在某个图层上、经过定位或带动画的 scene;在某个图层上的 hide。 |
| Hard Pause、Window With Transition | 无法通过点击跳过的暂停;带转场地显示、隐藏对话窗口或将其设为自动。 |
| Show Screen、Hide Screen、Call Screen | 一个界面:在故事继续的同时覆盖在游戏上方、将其移除,或者显示并等待其应答,应答随后位于 _return 中。每个节点接受一个实参,类型为数字、true、false 或文本。 |
| Play With Options、Queue | 带有脚本 play 所有选项(淡入、淡出、循环、音量、if_changed)的音频;排队在当前播放内容之后播放的文件。 |
| Voice Sustain、Movie With Options | 让语音在下一句台词期间继续播放;一部可以让音乐保持播放的影片。 |
| Input With Options | 带有默认值、长度限制和允许字符的输入。 |
| Menu From Choices | 一个具有任意数量选项的菜单,选项用 Start Choices 和 Add Choice 收集,可选命名(以便跳转可以回到它),并可指定一个列表变量来隐藏已选过的选项,与 menu set 的做法相同。 |
| While | 在 Condition 成立时重复一个序列。 |
| Call With Arguments | 调用一个标签,为其参数传入最多三个实参,每个都指定类型。 |
| Jump To Variable、Call Variable | 跳转到或调用某个变量所指明的标签。 |
| Jump From Function | 跳转到某个 Function 图所给出名称的标签(见在 Function 图中实现逻辑)。 |
| Return Value、Return Variable | 以一个指定类型的值或某个变量的值从调用中返回,调用方将其读作 _return。 |
| Run Command With Values | 一个 run 命令,带有最多三个实参,每个都指定类型。 |
| Python Block | 一个 Python 块,行数不限,在序列执行到此处时针对故事的变量运行;它所设置的、故事能够保存的内容会写回故事。此包自带 Python,因此文档无需为此做任何额外准备。 |
Choice
Make Choice 由文本、其后续的 Sequence 以及一个可选的 Condition(例如 Variable At Least(affection、3))构建一个菜单选项;没有条件时,该选项总会显示。选项输入到 Menu 节点,或输入到用于 Menu From Choices 的 Add Choice。Choice With Id 为选项指定一个作者自选的 id,这样即使选项文字改写了,它仍会被记为已选。
Conditions
Condition 是故事所做的一次判断,由各个部分构建而成,而不是写成文本。它可以输入到 If、While、Make Choice、Scene Choice、Branch To Scene、Set To Condition,以及图像、图层、音乐曲目、画廊条目和界面元素的条件中。它会变成与脚本 if 中相同的表达式,因此可以存档、回滚、接受静态检查,并能读回为脚本;唯一的例外是 Condition From Function,它没有脚本形式。
| 节点 | 何时成立 |
|---|---|
| Always | 始终成立。 |
| Compare Variable | 变量与某个值的比较结果成立:==、!=、<、<=、> 或 >=。该值的类型为数字、true、false、none 或文本。 |
| Variable At Least | 一个数字变量至少为某个值。 |
| Variable Is Set | 变量有值:为真、非零、非空。 |
| Has Visited | 在本周目中已到达过某个标签或场景。 |
| List Contains | 一个列表变量包含某个值,例如某件礼物在已送出的礼物之中。 |
| All Of、Any Of | 两到四个条件全部成立,或至少一个成立。 |
| Not | 某个条件不成立。 |
| Compare Variables | 两个变量的比较结果成立:trust > doubt。 |
| Visited At Least | 在本周目中到达某个标签至少达到指定次数:例如第三次去集市。 |
| Ever Reached | 在任意周目中曾到达过某个标签,例如一个曾经见过的结局。 |
| Has Seen Image、Has Heard | 在任意周目中曾看到过某个图像,或听到过某段音频。 |
| Has Achievement | 已获得某项成就。 |
| Is Showing | 带有某个标记的图像此刻在舞台上。 |
| In Language | 玩家以某种语言阅读游戏,例如 zh-Hans。 |
| By Chance | 以 0 到 1 的概率随机成立,抽取方式保证回滚后结果相同。 |
| Condition From Function | 一个接收故事变量的成员 Function 图给出 true 或 false;见在 Function 图中实现逻辑。 |
| Condition From Script | 以脚本形式编写的条件,例如 affection >= 3 and gift == "fan",用于其他节点无法表达的情况。 |
Effects
Effect 是对故事变量的一次更改。Apply Effect 在序列中的某一点进行更改,Scene Choice 则在选项被选中时进行更改。效果会变成与脚本 $ 行相同的赋值,因此会随故事一起回滚和存档。
| 节点 | 更改的内容 |
|---|---|
| Set Variable | 将变量设为一个指定类型的值。 |
| Add To Variable | 对数字变量做加法(除非另行指定,否则加 1)。 |
| Add To List | 向列表变量添加一个值。 |
| Subtract From Variable | 对数字变量做减法(除非另行指定,否则减 1)。 |
| Remove From List | 从列表变量中移除一个值。 |
| Change Variable | 用运算符更改变量:=、+=、-=、*=、/=、//= 或 %=。 |
| Change Variable By Variable | 用另一个变量的值、以相同的运算符更改变量:bag -= chosen 从包中取出所选的礼物,coins += reward 加上一份奖励。 |
| Copy Variable | 将变量设为另一个变量的值。 |
| Toggle Variable | 将为真的变量变为假,为假的变为真。 |
| Random Number | 将变量设为介于最小值和最大值之间(包含两端)的整数,抽取方式保证回滚后结果相同。 |
| Set To Condition | 将变量设为某个 Condition 此刻是否成立。 |
| Number From Function、Text From Function | 将变量设为某个成员 Function 图根据故事变量给出的数字或文本。 |
| Effect From Function | 按某个过程式图(例如 Routine)的指示,用 Set Number、Set Text、Set Flag 和 Add 更改故事的变量。 |
| Combine Effects | 两到四个效果,按顺序执行。 |
| Effect From Script | 以脚本形式编写的更改,相当于 $ 之后的部分:affection += 1。 |
Story Variables
交给 From Function 节点的图所接收到的东西:故事的变量,作为一个 Story Variables 值。
| 节点 | 作用 |
|---|---|
| Number、Integer、Text、Flag、List | 将变量读取为数字(未设置时读作 0)、整数、文本(未设置时读作空)、是否有值(为真、非零、非空),或列表的各项。 |
| List Contains | 列表变量是否包含某个值,按文本比较。 |
| Visited | 在本周目中是否进入过某个标签。 |
| Set Number、Set Text、Set Flag、Add | 将变量设为数字、文本或布尔值,或对数字做加法。 |
它们是 Story Variables 的成员,Divooka 根据成员的行为决定在哪些图中提供它。Function 图是数据流:它可能在任何需要其结果的时候被求值,并且结果会被缓存,因此它只接受不改变任何东西的成员。读取类成员标记为 const,这告诉 Divooka 它们不改变任何东西,因此它们既出现在 Function 图中,也出现在过程式图中。写入类成员会改变故事,因此只在过程式图中提供,例如交给 Effect From Function 的 Routine 图。任何 C# 成员以节点形式出现在何处,都由同一条规则决定;见 C# 技巧。(过去代替读取类成员的 Read Number、Read Text、Read Flag、Read List Contains 和 Read Visited 节点已被移除;请使用这些成员。)见在 Function 图中实现逻辑。
Scenes
Scene 是编写游戏的配置式方法,也就是 Novella One 构建游戏的方式:每个场景指明一个背景、音乐、其台词以及故事接下来的去向,不需要手动连接标签、跳转或菜单。场景会变成一个以其名称命名的普通标签,因此可以存档、回滚并与标签和脚本混用,名为 start 的场景开始游戏。见概念。(Story Writing 节点 Scene 是另一回事:它是一条清空舞台并显示背景的语句。)
| 节点 | 作用 |
|---|---|
| Make Scene | 一个场景:其名称、作为 Sequence 的台词(Say、Show 和其他 Story Writing 节点)、一个结尾、一个背景(例如 bg plum_garden;为空时保留当前舞台)、背景的转场(默认为 fade)以及音乐(为空时保留正在播放的音乐)。没有结尾时,游戏在该场景之后结束。 |
| Go To Scene | 一种结尾:故事前往另一个场景。 |
| Choose Scene | 一种结尾:一句提示文字(可以为空)和两到四个 Scene Choice 值;由玩家选择下一个场景。 |
| Scene Choice | 选项的文本、它通向的场景、一个决定何时提供该选项的可选 Condition,以及一个在选中时执行的可选 Effect。 |
| Branch To Scene | 一种结尾:一个 Condition 和两个场景,条件成立时前往第一个,不成立时前往第二个。 |
| End Game | 一种结尾:游戏结束。 |
| Add Scene | 添加了一个场景的故事。 |
Animation
Animation 是一条 Ren'Py ATL 风格的时间线,一步一步地构建,就像在脚本中编写变换那样。它可以输入到 Show With Options 或 Scene With Options 中作为图像自己的动画,也可以输入到 Define Transform 中命名,供任何 at 使用。
| 节点 | 作用 |
|---|---|
| Start Animation | 一条可在其上构建的空时间线。 |
| Set Property | 立即设置一个属性:将 xalign 设为 0.5。 |
| Animate Property | 在若干秒内将一个属性以动画方式变化到某个值,由缓动函数(linear、ease、easein、ease_bounce 等)控制缓动。 |
| Also Change | 在前一步中同时更改另一个属性,使两者一起变化:例如同时移动和淡出。 |
| Wait | 在下一步之前等待。 |
| Repeat | 重新开始时间线或其所在的块:重复指定次数,为 0 时无限重复。 |
| Animation Block | 嵌套在本时间线中的一条时间线,使其中的重复只循环这一部分。 |
| In Parallel | 同时运行的两条或三条时间线,每条为自己的属性制作动画。 |
| Include Transform | 就地运行另一个变换,例如 left 或故事定义的某个变换,并可带实参。 |
| Show Picture | 从此处起图像显示这张图片,如果给出了转场,则在转场时长内交叉淡化:用于眨眼、烛火、飘扬旗帜的各帧。 |
| Choose Between | 两条或三条时间线之一,每次动画执行到此处时按权重随机选取;同一个已显示图像在同一时刻总是选取相同的一条。 |
| At Time | 后续内容在动画开始后的这么多秒开始,并截断正在运行的内容。 |
| On Event | 图像被显示、替换或隐藏时的时间线(show、replace、hide,或以逗号分隔的多个)。如果图像的动画处理了 hide,它会留在舞台上直到该时间线结束,因此可以淡出或滑出。 |
| Contains Picture | 绘制在此图片内部的另一张图片,可带有自己的时间线:例如灯笼上的闪光。 |
| Call Transform Function | 将图像交给游戏为动画注册的一个 C# 函数处理,持续时间与该函数相同。 |
| Define Transform | 添加了一个具名变换的故事,该变换可以接受参数,例如 height=20。 |
| Add Animated Image | 添加了一个动画图像的故事:由 Show Picture 步骤和等待组成,无论显示在哪里都会播放动画,与脚本的 image name: 块相同。 |
| Animation From Script、Animation To Script | 以脚本行编写的时间线,以及以脚本形式显示的时间线。 |
值的写法与脚本相同:0.5 是舞台尺寸的比例,100 是像素,(-height) 读取变换的一个参数。
Images
供 Add Image From Source 使用的图像源,以及供 Add Layered Image 使用的分层图像:
| 节点 | 创建的内容 |
|---|---|
| Image File | 游戏文件中的一个图像文件。 |
| Color Image | 一种纯色,除非给出尺寸,否则填满整个屏幕。 |
| Movie Image | 作为图像播放的视频,除非另行指定,否则循环播放。 |
| Filtered Image | 应用了滤镜的图像:缩放、翻转、着色、去饱和、亮度、模糊和不透明度。 |
| Composite Image | 在一块指定尺寸的画布上,按偏移量绘制一个底图和最多两个其他图像。 |
| Switch Image | 在某个 Condition 成立时显示一个图像,否则显示另一个,在绘制时决定:例如白天和夜晚的房间。 |
| Start Layered Image、Add Always Layer、Add Layer Attribute | 一个分层图像:一个标记、始终绘制的图层,以及分组的属性,每个属性带有一个文件、是否为该组的默认值、该组是否允许同时选中多个,以及一个可选的 Condition。 |
Transitions
每个节点都生成任何节点的 with 所接受的转场文本,并填好其时间参数:Dissolve(秒数)、Fade(淡出、保持、淡入时间和颜色)、Flash、Image Dissolve(灰度遮罩、秒数、渐变范围和方向)、Move(秒数和缓动函数)以及 Timed Transition(任何内置或已定义的转场,使用自己的时长,例如两秒的 slideleft)。
Screens
Screen Layout 是以数据形式表示的自定义界面,是脚本 screen 定义的节点形式:放置在舞台上或在容器、分支和循环中布局的元素,每一帧都根据故事的变量重新计算。Add Screen 将它放入故事,Show Screen、Hide Screen 和 Call Screen 按名称使用它。
| 节点 | 作用 |
|---|---|
| Start Screen | 一个界面,带有名称、是否使其后方的游戏画面变暗(默认开启)、背景、显示的界面是否截获所有点击(模态),以及写法与标签参数相同的参数(question="Who?")。 |
| Start Contents | 一组本身不构成界面的元素,用于填充容器、分支或循环;它和界面一样用 Add 节点构建。 |
| Add Screen Text | 文本,可以用 [name] 显示变量和参数,带有字号、颜色和换行宽度。 |
| Add Screen Image | 一张图片:图像名称或文件,带有缩放比例。 |
| Add Screen Button | 一个带文字的按钮,包括按下时执行的操作、以像素为单位的尺寸(0 表示适应文字),以及一个用于 sensitive 的 Condition:仅在其成立时可按,否则显示为灰色。 |
| Add Picture Button | 一张可按的图片,例如地图上的一个地点,指针悬停时显示另一张图片,并带有一个用于 sensitive 的 Condition。 |
| Add Screen Bar | 一个相对于最大值显示变量的进度条,例如满分十分的信任度。 |
| Add Screen Input | 一个将输入写入变量的文本框,带有长度限制、允许的字符和宽度。 |
| Add Screen Space | 一块指定尺寸的空白区域,用于在容器中隔开各项内容。 |
| Add Screen Timer | 经过若干秒后执行,就像限时选项到时一样;设为重复时,每次到时都执行。 |
| Add Screen Key | 界面显示期间按下某个键时执行:escape、enter、space、left、一个字母、f1。 |
| Add Box | 一个容器,装着来自 Start Contents 的元素,按种类排列:vbox 上下排列,hbox 左右并排,grid 按指定列数分行,frame 放在一个使用主题颜色的面板上,fixed 各自放在其位置所指定的地方,viewport 上下排列、超出部分被裁剪并可用滚轮滚动。它接受间距、内边距、背景颜色或图像,以及尺寸(0 表示适应其所含内容)。 |
| Add For Each | 对一个列表变量中的每一项,各生成一次来自 Start Contents 的元素,该项放在其自己的变量中:例如为 bag 中的每件礼物生成一个按钮。 |
| Add If | 在某个 Condition 成立时显示一些元素,并可选在其不成立时显示另一些元素。 |
| Add Use Screen | 就地使用另一个界面的元素,例如多个界面共用的状态栏,其参数的实参按脚本的写法填写,以逗号分隔("Wen", trust + 1)。它们是表达式,因此 Add For Each 的行可以把它的项传下去。 |
| Add Screen Variable | 界面自己的变量及其初始值(按输入的类型);界面的按钮可以更改它,它与界面同存续。 |
| Anchor Last Element | 以元素自身中心以外的某个点来放置最后添加的元素,该点以 X Anchor 和 Y Anchor 表示其尺寸的比例:(0, 0) 将其左上角放在其位置上,(1, 1) 则是右下角。结合元素自身的位置,这就相当于脚本的 anchor,进而相当于其 pos 和 align:(1, 1) 放在 (1, 1) 处会使元素位于角落,与 align (1.0, 1.0) 相同。在 vbox、hbox 或 grid 中,它改为在元素所占的位置内对齐该元素。 |
| Style Last Element | 为最后添加的元素指定故事中的一个样式,相当于脚本的 style "card";对于容器,还可为其所含内容的样式指定前缀,相当于 style_prefix "gold"。样式为空时保留其种类的样式。 |
| Set Last Element Style Property | 在最后添加的元素本身上设置一个样式属性,叠加在其样式之上,值按脚本的写法填写:outline_width 2。在按钮上,以 text_ 开头的属性用于设置其标签文字的样式:text_color "#ffffff"。 |
| Set Screen Style Prefix | 界面上所有内容的样式前缀:使用 gold 时,如果故事中有 gold_button 和 gold_button_text,界面的按钮就用前者绘制,按钮的标签文字用后者绘制。 |
| Screen To Script | 以脚本形式写出的界面。 |
容器、分支或循环包含一条 Start Contents 链,该链本身也可以包含容器,因此界面是在 Add Screen 之下以一棵输入树的形式构建的。直接位于界面中、fixed 或 frame 中的元素接受一个位置,而位于 vbox、hbox、grid 或 viewport 中的元素由其容器放置。0 到 1 之间的位置表示区域的比例,大于 1 的位置表示像素,因为节点中的数字没有小数点可供区分;除非 Anchor Last Element 指定以另一个点放置,否则每个元素都以其位置为中心。大多数元素接受一个可选的 Condition,仅在其成立时显示。
按钮、计时器或按键执行的操作是一个 Button Actions 值:
| 节点 | 作用 |
|---|---|
| Answer | 以一个值(按输入的类型)应答被调用的界面,故事将其读作 _return。 |
| Go To Label | 关闭界面,并在某个标签处继续故事。 |
| Set Variable Action | 将变量设为一个值(按输入的类型):如果界面自己有同名变量则设置它,否则设置故事的变量。 |
| Set Variable To Action | 在按下按钮时将变量设为某个表达式的结果,例如 Add For Each 行中的项:item。 |
| Toggle Variable Action | 将为真的变量变为假,为假的变为真。 |
| Notify Action | 显示一条通知。 |
| Play Sound Action | 播放一次音效。 |
| Open Menu Action | 打开游戏的某个菜单:save、load、preferences、history、gallery、music_room、achievements 或 main_menu。 |
| Close Screen | 关闭界面;被调用的界面应答为空值。 |
| Show Screen Action、Hide Screen Action | 显示或隐藏另一个界面。 |
| Run Command Action | 运行游戏注册的一个命令。 |
| Then Action | 先执行一个操作,再执行另一个:例如先设置变量,再前往某个标签。 |
Story
| 节点 | 作用 |
|---|---|
| Make Label | 由名称和序列构成的标签;游戏从 start 开始。 |
| Start Story | 一个空故事。 |
| Add Label、Add Character、Add Image、Add Default | 多了一个标签(替换同名标签)、角色、图像或变量默认值的故事。 |
| Make Label With Parameters | 一个接受实参的标签,参数按脚本标签行的写法填写(who, times=1),由 Call With Arguments 传入,标签将其作为变量读取。 |
| Add Image From Source、Add Layered Image | 添加了一个来自 Images 节点的图像或一个分层图像的故事。 |
| Add Transition | 添加了一个由内置转场构成的具名转场的故事:例如将 slow 定义为 dissolve(2)。 |
| Add Audio Name、Add Channel、Add Music Track | 添加了一个音频文件名称、一个自己的音频通道,或一首音乐空间曲目(在听到时或某个 Condition 成立时解锁)的故事。 |
| Add Constant | 添加了一个常量的故事,该常量在游戏开始时设置,并在读档后再次设置,因此更改后的值也会作用于旧存档。 |
| Add Gallery Entry When | 添加了一个画廊条目的故事,该条目在某个 Condition 成立时解锁,而不是在其图片被看到时解锁。 |
| Add Screen | 添加了一个来自 Screens 节点的界面的故事。 |
| Add Style | 添加了一个来自 Styles 节点的样式的故事:一种自己的外观,或者在使用 Novella 自带名称(例如 say_window)时,对游戏相应部分的细化。同名的第二个样式会叠加到第一个之上。 |
| Add Python Definitions | 添加了在游戏开始时运行一次的 Python 的故事,相当于脚本的 init python::其中定义的函数可以在任何条件、文本或效果中调用,例如 [title_for(trust)]。 |
| Translate Line、Translate Line By Id、Translate String | 添加了翻译的故事:翻译一句台词(通过其标签、说话者和文本,或通过 Say Line 为其指定的 id 查找),或翻译任何其他文本:选项、名称、提示、通知。 |
| Story From Script | 从脚本文本读取的故事。 |
| Story From Library | 从文档库中的脚本文件读取的故事。 |
| Combine Stories | 将两个故事合为一个。 |
| Story To Script、Label Names | 以脚本文本表示的故事,以及其标签的名称。 |
| Check Story | 单独检查一个故事:未定义的标签、角色、变换和转场。 |
Characters
Make Character 由 id、显示名称、名称颜色、图像标记,以及是否使用侧边图像或在 NVL 页面上说话来创建一个角色。Style Character 返回一个使用不同文本颜色、字体或引号的副本。
另有两个节点为角色的台词赋予自己的外观,相当于脚本的 window_style 和 what_size 40(见样式)。台词的组成部分有 window(对话窗口)、namebox(名称标签)、who(名称)和 what(台词)。
| 节点 | 作用 |
|---|---|
| Character Part Style | 一个角色,其台词的某一部分使用故事中的一个样式绘制:window 使用 ghost_window。 |
| Set Character Style Property | 一个角色,其台词的某一部分上设置了仅对该角色有效的样式属性:what、size、40,或 window、background、"#200000"。 |
Styles
Style 是用于文本和容器的具名外观,是脚本 style 定义的节点形式:一组属性,带有一个用于继承其余属性的父样式,以及针对元素所处各种状态的变体。Add Style 将它放入故事。与 Novella 自带样式(say_window、say_dialogue、choice_button、menu_button 等)同名的样式会细化每个游戏界面的相应部分;任何其他名称都是故事自己的外观,可由界面元素、角色或台词中的 {style=name} 片段指明。
| 节点 | 作用 |
|---|---|
| Start Style | 一个带有名称和可选父样式的样式:例如由 button 派生的 gold_button。 |
| Set Style Property | 设置了一个属性的样式,值按脚本的写法填写(颜色要连同引号写成 "#ffd27a",以及 40、(24, 12)、true、center),作用于所有状态,或在给出状态时只作用于该状态:hover、selected、insensitive、idle 或 visited。不是样式属性的名称会被立即拒绝。 |
| Style To Script | 以脚本形式写出的样式。 |
| Built-in Style Names | Novella 自带样式的名称,以逗号分隔:即故事可以重新设置样式的部分。 |
Start Style(
gold_button、button)→ Set Style Property(background、"#3a2a10")→ Set Style Property(background、"#6a4a18",状态hover)→ Set Style Property(corner_radius、12)→ Add Style
样式列出了每个属性和每个内置样式,并说明了如何沿着样式的父样式和状态查找属性。
Game Definition
| 节点 | 作用 |
|---|---|
| Make Game Definition | 以数据形式表示的完整游戏:标题、故事、宽度和高度。 |
| With Config、With Theme | 更改了一项设置的副本,该设置优先于故事自身对同一键的 define。 |
| With Library Assets | 从文档库读取文件的副本,可选同时将库中的脚本加载到故事中。 |
| With Asset Folder | 从某个文件夹读取文件的副本。 |
| With Story | 添加了另一个故事内容的副本。 |
| Check Game | 静态检查,同时对照文件检查故事。 |
| Run Game Definition | 在窗口中运行一个定义:函数式描述的过程式终点。 |
函数式节点返回新值,从不更改其输入,因为数据流图会缓存值,并且可能把同一个值交给两个分支。
Routine 图
Routine 图就是以节点形式表示的 C# 程序。在其执行路径上:

- Make Novella Game:标题
The Red Thread,宽度1920,高度1080。 - Add Library Assets:游戏的图像、声音、字体和脚本,嵌入在文档中。
- Load Scripts From Assets:文件夹
scripts。 - Set Config:
autosave_slots、8。 - Run。
运行这个图,游戏就会在窗口中打开。用 Validate 代替 Run,再打印其结果,就是静态检查。
Register Command 将一个 run 命令绑定到一个没有输入也没有结果的函数,适用于只做某件事的命令,例如写日志或打开网页。Register Command Function 将命令绑定到一个以文本形式接受实参并给出应答的函数,Register Expression Function 则对故事表达式所调用的函数做同样的事。命令也可以由 Events 图的 Command 事件来应答,The Red Thread 的 run fortune 就是这样处理的。
Events 图
在 Events 图中,游戏是一个类。将图的基类型设为 Novella Application(一个 Novella Game),游戏的回调就会成为图可以实现的事件,而每个 Novella Game 节点都作用于图自己的游戏,无需连接接收者:

| 事件 | 何时运行 |
|---|---|
| Setup | 在窗口打开之前运行一次:设置、资源、脚本、定义。 |
| Script | 如果实现了它,开始游戏时会将其作为实时脚本运行,而不是从故事的起始标签开始。 |
| Command | 用于没有任何人注册的 run 命令:它接收名称和文本形式的实参,不返回任何值;它用 Set Result 应答,故事将应答读作 _return(未设置时为空值)。 |
| Label Entered | 每当故事进入一个标签时。 |
| Game Ended | 故事结束时,在返回主菜单之前。 |
只有图实际实现的事件才会生效,因此不实现 Script 就会让故事的标签掌控流程,不实现 Command 则会使未知命令成为错误。
The Red Thread 的文档这样应答其故事中的 run fortune:
Command → Set Result,其输入是对一组签文中某一张签的 Translate Text,该签由 Random Integer 在 0 到最后一个索引之间选出
一个从文档库加载脚本的 Setup 就构成了一个完整的游戏:
Setup → Set Resolution(1920, 1080)→ Add Library Assets → Load Scripts From Assets(
scripts)
要运行它,运行这个图即可;此包为 Novella Application 注册的 Novella 框架会打开窗口。Plugins > Frameworks > Novella > Run Novella Game 菜单项对文档的入口图做同样的事。
以节点形式编写的实时脚本
实现 Script 会使该事件的执行路径成为故事本身。Say、Show、Scene 等节点逐个播放,并在玩家作出应答后继续;Menu 返回所选项的索引,供 Branch 或 Switch 使用,Input 返回输入的内容:

Script → Scene(
bg garden、fade)→ Show(qinglan smile、right、dissolve)→ Say(q、What hangs in the treetop?)→ Menu(A gong、The moon)→ 根据 choice = 1 进行 Branch → Say(q、The moon. Correct.)
这是用节点编写小游戏最直接的方式。它的局限是明确说明的,而非隐藏的:图的执行位置不是数据,因此实时脚本无法存档、读档或回滚。Call Label 可以从实时脚本中运行一个故事标签,但只要实时脚本仍在其下层运行,存档和回滚就仍不可用。对于玩家会存档的游戏,请将故事写成标签或场景(用脚本或函数式节点均可),并将 Events 图留给设置和命令。见概念。
函数式故事构建
函数式节点无需任何脚本即可编写故事。一个标签就是一条链:

Start Sequence → Scene(
bg garden、fade)→ Show(qinglan smile、right、dissolve)→ Say(q、You came.)→ Menu(提示文字,带有自己序列的 Make ChoiceI did.,带有自己序列的 Make ChoiceI was passing.)→ Make Label(start)
而故事负责收集标签和定义:
Start Story → Add Label(start)→ Add Label(garden)→ Add Character(Make Character
q、Qinglan、#c0392b、图像qinglan)→ Add Default(affection、0)→ Make Game Definition(The Garden、故事)→ With Library Assets → With Theme(text_size、40)
逻辑也无需脚本。基于变量的分支和对变量的更改同样是节点:
Apply Effect(Add To Variable
affection、1)→ If(Variable At Leastaffection、3;then 为包含 Sayq、Then stay.的序列;otherwise 为包含 Sayq、Then go.的序列)
而只在某些情况下才提供的选项,则在其 Make Choice 上接受一个条件:All Of(Has Visited market,Not(Variable Is Set gift))。
两种风格可以自由混用:在节点用起来不顺手的地方,Story From Script 或 Script 节点可以引入脚本文本,Condition From Script 和 Effect From Script 也可以为单个判断或更改做到这一点;Combine Stories 将脚本编写的故事与节点构建的故事合并;Story To Script 则将其中任何部分显示为脚本。由条件和效果节点构建的故事,打印出的脚本与人手写的相同,并且该脚本读回后也完全相同。
由节点构建的标签不来自任何文件,因此不会像脚本中的标签那样落入下一个标签:执行到其末尾即结束游戏,或返回到其调用方。应当继续往下走的标签要以 Jump、Call 或 Return 结尾。当一个由节点构建的标签可能执行到末尾、而它后面的标签也是由节点构建的时,静态检查会发出警告,因为脚本中的标签在那里会落入下一个标签。
在 Function 图中实现逻辑
当 Conditions 和 Effects 节点无法组合出所需的判断或更改时,可以改用 Function 图,同样无需脚本。Condition From Function、Number From Function、Text From Function 和 Jump From Function 各自接受文档中的一个成员 Function 图,该图接收故事的变量(一个 Story Variables),用其 const 成员 Number、Text、Flag 以及其余 Story Variables 节点读取这些变量,并给出应答:true 或 false、一个数字、一段文本,或要跳转到的标签名称。Effect From Function 接受一个过程式图(例如 Routine),该图用 Set Number、Set Text、Set Flag 和 Add 更改变量;由于这些成员会改变故事,Function 图不提供它们。该图在节点的函数引脚上指定,与任何委托引脚指定函数的方式相同。
一个成员 Function 图 Is Generous,带有一个类型为 Story Variables 的输入
Variables:Number(Variables、kindness)→ Bigger Than Or Equal(2)→ 其输出Holds。在故事中:If(指定 Is Generous 的 Condition From Function;then 为包含 Say 的序列)
该图读写的是故事的变量,因此其更改会随故事一起存档、读档和回滚。不过,与 C# lambda 一样,函数没有脚本形式:Sequence To Script 会在其位置显示一段描述,静态检查无法查看其内部,而使用了它的故事也无法从其脚本读回。只要 Conditions 和 Effects 节点能够表达,就请使用它们。
Novella Game 上的 Register Command Function 和 Register Expression Function 为脚本做同样的事:由图实现一个接受实参并给出应答的 run 命令,或一个供故事表达式调用的函数。
以场景构成的游戏
Scenes 节点无需手动连接标签、跳转或菜单即可编写游戏。每个场景就是一个 Make Scene:
Make Scene(
start;台词 Start Sequence → NarrateA bridge in the rain.;结尾 Choose Scene(Cross it?,Scene ChoiceCross.→far_side,效果为 Add To Variablecourage,Scene ChoiceWait.→near_side);背景bg bridge)
Make Scene(
far_side;台词 NarrateThe far side.;结尾 Branch To Scene(Variable At Leastcourage、1;brave;near_side))
而故事负责收集这些场景及其使用的变量:
Start Story → Add Default(
courage、0)→ Add Scene(start)→ Add Scene(far_side)→ Add Scene(near_side)→ Add Scene(brave)→ Make Game Definition
在过程式图中,则改用 Novella Game 节点 Add Scene 将每个场景添加到游戏中。
描述、预览、运行
推荐的工作流将游戏的描述放在一处,并从两处运行它:

- 在 Function 图中描述游戏。用函数式节点构建故事、角色和设置,并从图中返回一个 Game Definition。
- 从 Flows 图调用它以进行预览。将该函数放入一个 Flows 图,选中其输出,然后在 Properties 面板中打开 Custom Preview(自定义预览)。游戏会在自己的窗口、自己的线程中运行,存档保存在内存中(预览从不触及玩家的真实存档),并开启开发者控制台(Shift+O;
jump label可直接跳转),即使游戏的脚本将developer_mode设为 false 也是如此。 - 将同一个定义交给过程式图来真正运行它:Make Novella Game From Definition → Run,或 Run Game Definition。
Custom Preview 能运行的不只是完整的游戏。没有 start 标签的 Story 从其第一个标签开始,Label 作为单标签故事运行,Sequence 作为 start 标签运行,Scene 作为单场景故事运行,Novella Game 运行其定义,因此链上的任何节点都可以单独试运行。不使用自定义预览时,普通预览会将值显示为脚本:序列、标签或故事显示为其对应的脚本,角色显示为其 character 行,游戏定义则显示为一段摘要及其后的脚本。
文档中的资源
Divooka 文档可以携带其游戏:嵌入在文档资源库中的图像、声音、字体、视频和脚本(在 Divooka Compute 中为 Asset library,快捷键 Ctrl+Shift+A,使用 Import files… 导入)。库中的路径就是脚本所引用的路径,因此请保留脚本所期望的文件夹结构(bg/、sprites/qinglan/、audio/bgm/),并且图像在库中按其路径命名的方式与在磁盘上完全相同。
| 风格 | 节点 | 读取的内容 |
|---|---|---|
| 过程式 | Add Library Assets | 库中的每个文件都成为游戏的一个文件;之后接上 Load Scripts From Assets 以加载脚本。 |
| 函数式 | With Library Assets | 对定义做同样的事;打开其脚本选项,即可同时将库中的脚本加载到故事中。 |
| 函数式 | Story From Library | 库中的一个脚本文件,作为故事。 |
这三个节点都会自动获取文档的库。库文件和文件夹可以组合使用:对于同一路径,后添加的来源优先,因此在测试时,在库之后添加的文件夹可以覆盖库中的部分文件。
在 Asset library 中选中一个文本资源,会在预览窗格中显示其前几行,而 .novella 脚本也算作文本,因此无需导出,就能在那里分辨和阅读游戏的脚本。
Divooka 无法涉及的部分
脚本语言能表达的一切都有对应的节点,接受实参的命令和表达式函数也是如此,但有一个区别:节点传给标签、命令或界面的实参,是在图构建故事时就固定下来的值,而脚本中的实参是在播放时才求值的表达式。Add Use Screen 是个例外,它接受表达式。少数 C# 扩展点没有对应的节点:以 C# 类编写的界面(RegisterScreen),用于 Screens 节点无法布局的任何内容;动画的 function 语句所调用的函数(RegisterTransformFunction),由 Call Transform Function 指定其名称;直接处理 Value 和运行器而非文本的命令和函数;以及 Events 图之外的实时脚本。关于这些内容,见开发。