跳到主要内容
  1. Posts/

自用 ink 语法总结

至于 ink 是什么,因为我是懒蛋就不写了,请看文内链接。

·· 3980 字·8 分钟

ink 简介(抱意思真就这么懒,一个字不肯多写了……)以及 inklewriter

本篇包括了 ink 的大多数基础语法,并在进阶内容部分标注了官方文档的链接,仅建议在对 ink 有所了解的情况下作为快速查找的索引使用。本篇不涉及任何开发相关内容。

注释(comment) #

// 单行注释

/*
多行注释
*/

TODO: 任务提醒

选项(choice) #

一次性选项使用星号。

这是一段正文
    * 第一个选项
    第一个选项后的文字
    * 第二个选项
    第二个选项后的文字
// 回退选项(fallback choice)
    * -> next // 当所有选项都被选择过,没有选项剩余可供选择时,该选项会自动触发,跳转至`next`部分

// 另一种结束手段,直接跳转文字
    * ->
    这里没有更多选项了
-> DONE

(关于DONE,参见官方文档。)

会重复出现的选项使用加号。

这是一段正文
    + 第一个选项
    第一个选项后的文字
    + 第二个选项
    第二个选项后的文字

选项与输出的显示 #

默认情况下,选项会出现在内容文本中,就像是整个故事的一部分。如果不希望显示选项,用方括号包裹即可。

这是一段正文
    * [第一个选项] // 后续文字出现时不会和这行文字一起
    第一个选项后的文字
    * [第二个选项] // 后续文字出现时不会和这行文字一起
    第二个选项后的文字

另外,当选项代码呈现为AAA[BBB]CCC格式时,AAA部分选项和输出均显示,BBB部分只有选项中显示,CCC部分只有输出中显示。即选项显示为AAABBB,输出显示为AAACCC

而当选项代码呈现为[BBB]CCC格式时,则选项显示为BBB,输出显示为CCC

条件检查 #

这是一段正文
    * {sample_knot} 第一个选项
    第一个选项后的文字
    * {sample_stitch} 第二个选项
    第二个选项后的文字

访问过sample_knot时,才会出现选项一。访问过sample_stitch时,才会出现选项二(关于 knot 和 stitch,详见后文。)

这是一段正文
    * {not sample_knot} 第一个选项
    第一个选项后的文字
    * {not sample_stitch} 第二个选项
    第二个选项后的文字

未访问过sample_knot时,才会出现选项一。未访问过sample_stitch时,才会出现选项二。

这是一段正文
    * 第一个选项
    第一个选项后的文字
    * {sample_knot} {sample_stitch} 第二个选项
    第二个选项后的文字

条件可以叠加。

这是一段正文
    * 第一个选项
    第一个选项后的文字
    * {sample_knot > 2} 第二个选项
    第二个选项后的文字

访问sample_knot大于 2 次时,才会出现选项二。

结点(knot)& 跳转(divert) #

结点由两个或更多等号表示,末尾的等号非必要。

=== 这是一个结点 ===
实际命名结点时,需要使用没有空格的单个英文词
-> END // 跳转至故事结束

这是一段正文
* 第一个选项
第一个选项后的文字
-> choice_one // 跳转至结点“choice_one”
* 第二个选项
第二个选项后的文字
-> choice_two // 跳转至结点“choice_two”

结点之间不能重名。每个结点的末尾,都必须有一个跳转。

// 这样的语法也成立,区别在于最终呈现时,两句话不分行。
这是一段正文 -> next

=== next ===
这是另一段正文

INK 始终从文件的顶部开始,然后一直向下运行。但如果你将所有内容拆分为结点,则务必确保在文件顶部至少有一个初始跳转(如-> top_knot),以告诉它以哪个结点开始。修改初始跳转,可更方便的进行测试。

胶水(glue) #

在句首或句尾使用<>,同样会使得后接或前接的句子强制不换行。

子结点(stitch) #

=== 这是一个结点 ===
实际命名结点时,需要使用没有空格的单个英文词
-> next

= 这是一个子结点
实际命名时,同样需要使用没有空格的单个英文词
-> END

一个结点内部的子结点不能重名。

作用域(scope) #

在结点外部时,需要使用结点.子结点的格式才能跳转,如下所示:

-> sample_knot.sample_stitch

=== sample_knot ===
这是一段正文
-> sample_stitch

= sample_stitch
这是另一段正文
-> END

编织(weave) #

聚集(gather) #

作用是将分支剧情收拢到同一个后续剧情上。

聚集前:

* 选项 A
剧情 A
-> 后续结点
* 选项 B
剧情 B
-> 后续结点

=== 后续结点 ===
后续剧情

聚集后:

* 选项 A
剧情 A
* 选项 B
剧情 B

- 后续剧情
无需短横线,继续编写剧情即可

* 选项 C
剧情 C
* 选项 D
剧情 D

- 聚集也可以连续使用

嵌套流程(nested flow) #

作用是在分支剧情上继续展开分支剧情。

* 选项 A
剧情 A
    ** 选项 A1
    剧情 A1
    ** 选项 A2
    剧情 A2
    -- 后续剧情 A // 也可以加入聚集
* 选项 B
剧情 B

- 后续剧情

选项和聚集能够无限嵌套,只要在每一层使用对应数量的星号和短横线即可。

追踪(tracking)& 标签(label) #

标签(label_name)用于标记选项或剧情文本。

* (choice) 选项 A
剧情 A
* 选项 B
剧情 B

- (content) 后续剧情

标签可以用大括号检查是否访问过,也可以作为跳转的目标。

* {choice} 选项 C
剧情 C
* {content} 选项 D
剧情 D

剧情 1 -> choice
剧情 2 -> content

作用域(scope) #

如果标签在结点内被引用,则可以直接使用。如果标签在结点外被引用,则采用结点名.标签名的格式。如果标签在子结点内,但在结点外被引用,则采用结点名.子结点名.标签名的格式。如下所示:

=== knot_1 ===
- (gather_1)
    * {knot_2.stitch_2.gather_2} 选项 A

=== knot_2 ===
= stitch_2
    - (gather_2)
        * {knot_1.gather_1} 选项 B

变量(variable) #

VAR name = value // 全局变量
CONST name = value // 常量,无法更改
~ temp name = value // 局部变量,仅能在同一个结点中访问,结点外无法使用

// 在剧情中为变量赋值
这是一段正文
    ~ name = "名字"

// 在剧情文本中使用变量时,要用花括号将其包裹
我的名字是{name}

变量可存储的五种数据类型如下所示:

VAR string = "名字" // 字符串
VAR integer = 5 // 整数
VAR float = 3.14 // 浮点数
VAR boolean = true
VAR boolean = false // 布尔值
VAR divert = -> next // 跳转

变量可进行的数学运算包括:加法(+)、减法(-)、乘法(*)、除法(\)、取余数(%)。

变量可进行的逻辑运算包括:相等(==)、大于(>)、小于(<)、大于或等于(>=)、小于或等于(<=)、与(and&&)、或(or||)、非(not,不建议使用!)。

// 组合使用可以形成十分复杂的判定条件
{ conditionA and not conditionB:
    这是一段正文
}
{ (conditionA or conditionB or conditionC) and not conditionD:
    这是一段正文
}

任何使用花括号包裹的内容,都会进行相关的计算和判断。{ x > 1 }这样的代码会输出true或者false,也可以直接进行{x + y}{5 + 3}这样的运算。

逻辑运算(conditional logic) #

内联逻辑(inline logic) #

VAR variableOne = true
VAR variableTwo = 4

{variableOne:如果变量为true,这句话将出现|如果变量为false,这句话将出现}
{variableTwo < 5 :如果`variableTwo < 5`为真,这句话将出现|如果`variableTwo < 5`为假,这句话将出现}

多行逻辑(multi-line logic) #

VAR variableOne = true

{variableOne
    如果变量为true,这句话将出现
- else:
    如果变量为false,这句话将出现
}

关于 if-else(和同样适用于多行的 switch)语法,详见下文。

参数(parameter) #

类似变量的还有参数。

-> main

=== main ===
你选择的宝可梦是?
* [妙蛙种子]
    -> chosen("妙蛙种子")
* [小火龙]
    -> chosen("小火龙")
* [杰尼龟]
    -> chosen("杰尼龟")

=== chosen(pokemon) ===
你选择了{pokemon}
-> END

函数(function) #

// 定义
=== function add(a, b) ===
~ return a + b

// 调用
~ x = {add(5, 3)}

更多内容,详见官方文档

内置函数(built-in function) #

详见官方文档

RANDOM(min, max) // 随机创建数字,包括最小值和最大值

{INT(3.2)} is 3.
{INT(-4.8)} is -4.

{FLOOR(4.8)} is 4.
{FLOOR(-4.8)} is -5.

{FLOAT(4)} is still 4.

流程控制 #

if-else #

// if-else
{x > 0:
    ~ y = x - 1
- else:
    ~ y = x + 1
}

// if-else的另一种写法
{
    - x > 0:
        ~ y = x - 1
    - else:
        ~ y = x + 1
}

// 去掉else,仅做if判断
{x > 0:
    ~ y = x - 1
}

// 多个if判断
{
    - x == 0:
        ~ y = 0
    - x > 0:
        ~ y = x - 1
    - else:
        ~ y = x + 1
}

这同样可以应用于文本输出。

VAR know_about_wager = false
我盯着福克先生看。
{
    - know_about_wager:
        <>“但是您一定不是认真的吧?”我质问道。
    - else:
        <>“但是这次旅行一定有原因吧?”我观察到。
}
他没有回答。

switch #

判断变量 x 中的内容,还可以使用如下形式:

{ x:
- 0: zero
- 1: one
- 2: two
- else: lots
}

其他使用举例:

// 例1
VAR item = 30

你有<>
{ item:
- 1: 一个
- 2: 两个
- else: 很多
}
<>道具

// 例2
VAR item = 1

{ item:
- 1: -> part1
- 2: -> part2
- else: part3
}

=== part1 ===
你持有1号道具
-> END
=== part2 ===
你持有2号道具
-> END
=== part3 ===
你持有过多道具
-> END

标记(tag) #

本节大量复制修改了参考来源 3 的内容。

不与标签(label)混淆。更多相关内容,参见官方文档

插入图片 #

# IMAGE: imageName.jpg
# IMAGE: myImages/imageName.jpg // 使用相对路径

// 可以在行尾添加图片标记。该图像将始终显示在游戏文本上方。
上方有图片 # IMAGE: imageName.jpg

清空与重启 #

# CLEAR将清空当前显示的所有文本,从页面顶部重新开始。建议仅在选项后直接插入。如果插入到文本中间,则部分内容将在看到之前就被清除。

# RESTART将立即重置故事的进度(包括玩家选择的所有经历和变量),并从头开始。由于它会立刻生效,建议放在选项后。示例如下:

你死了
    * 从头来过
    # RESTART
    -> END

添加作者 #

# author: name放在 ink 文件的顶部。这将位于主标题的正下方。

深色主题 #

# theme: dark放在 ink 文件的最顶部。CSS 文件中的.dark选择器是深色主题的重写。

自定义 CSS #

// ink 文件
地上有一滩血。 # CLASS: danger

style.css文件中添加以下内容,以改变danger类的外观。

.danger {
  color: red;
}

其他 #

多个文件 #

使用INCLUDE引入另一文件的内容。

INCLUDE anotherFile

可变文本(variable text) #

详见官方文档

列表(list) #

详见官方文档

参考来源 #

  1. 【现代魔法系列】交互式小说编辑器 Inky 教程(一)以及同 UP 主后续的教程视频,为本篇的主参考源,对其举例进行了大量复制和修改。
  2. Learn Ink (video game dialogue language) in 15 minutes | Ink tutorial 以及汉化
  3. 用 Inky 编写基于 Web 的互动小说,官方新手指南的翻译,有关于定制外观和开发相关内容。
  4. 官方新手指南:Writing web-based interactive fiction with ink
  5. 官方详细文档:Writing with ink
  6. Inky 中文文档及教程:网站已经不再运行。鉴于直接阅读源代码十分不便,我没有细读,仅列在此处供查阅。