简体中文
English 简体中文 繁體中文
Methodox 首页

在 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# 程序。在其执行路径上:

Hello Novella:在 Routine 中创建游戏、提供脚本并运行

  1. Make Novella Game:标题 The Red Thread,宽度 1920,高度 1080。
  2. Add Library Assets:游戏的图像、声音、字体和脚本,嵌入在文档中。
  3. Load Scripts From Assets:文件夹 scripts。
  4. Set Config:autosave_slots、8。
  5. 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 节点都作用于图自己的游戏,无需连接接收者:

The Red Thread 的 Events 图:Setup 和 Command

事件 何时运行
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 事件

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 图留给设置和命令。见概念。

函数式故事构建

函数式节点无需任何脚本即可编写故事。一个标签就是一条链:

在 Flows 图中用 Story Writing 节点构建的场景

Start Sequence → Scene(bg garden、fade)→ Show(qinglan smile、right、dissolve)→ Say(q、You came.)→ Menu(提示文字,带有自己序列的 Make Choice I did.,带有自己序列的 Make Choice I 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 Least affection、3;then 为包含 Say q、Then stay. 的序列;otherwise 为包含 Say q、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 → Narrate A bridge in the rain.;结尾 Choose Scene(Cross it?,Scene Choice Cross. → far_side,效果为 Add To Variable courage,Scene Choice Wait. → near_side);背景 bg bridge)

Make Scene(far_side;台词 Narrate The far side.;结尾 Branch To Scene(Variable At Least courage、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 将每个场景添加到游戏中。

描述、预览、运行

推荐的工作流将游戏的描述放在一处,并从两处运行它:

返回 Game Definition 的 Function 图

  1. 在 Function 图中描述游戏。用函数式节点构建故事、角色和设置,并从图中返回一个 Game Definition。
  2. 从 Flows 图调用它以进行预览。将该函数放入一个 Flows 图,选中其输出,然后在 Properties 面板中打开 Custom Preview(自定义预览)。游戏会在自己的窗口、自己的线程中运行,存档保存在内存中(预览从不触及玩家的真实存档),并开启开发者控制台(Shift+O;jump label 可直接跳转),即使游戏的脚本将 developer_mode 设为 false 也是如此。
  3. 将同一个定义交给过程式图来真正运行它: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 图之外的实时脚本。关于这些内容,见开发。