基于Ink的简单互动文游制作

前言

本文是一个非常草台和简略的Ink互动文游制作讲解,作者也并不是很懂Ink。

Ink是一种Inkle Studio开发的编程语言,它是专门用来写互动故事的。

对于想要深入了解Ink的读者,且有一定英语水平,或不介意使用翻译工具的:

这里还是推荐去读官方的入门教程:Writing web-based interactive fiction with ink

还有官方的用户手册:ink/Documentation/WritingWithInk.md at master · inkle/ink

这显然会比我这种半吊子写的东西好得多。

Ink的语法十分简单,但其实也有很多高级特性。本文不深入探究,只是简单讲解一下环境部署,开发过程,基本语法,项目导出,部署上线的这个过程,也算是记一次经历。

部署方式:GitHub Pages。

这是一篇面向小白的教程,如果是有一定技术背景的用户,那么你基本可以完全忽略这篇教程的全部内容,你只要上手摸索几分钟就全懂了。


示例

一开始就是看到了这个StarInitial的「深渊凝视着你」- XPCheck。

仓库地址在这里:StarInitial/xpcheck

测试地址在这里,部署方式GitHub Pages,可以游玩:深渊凝视着你

就觉得挺有意思,于是学着做了一个。由于作者蓝枫是一个M男,做了一个「受虐癖测试」- MasoCheck。

仓库地址在这里:BlueMap1e/MasoCheck

测试地址在这里,部署方式GitHub Pages,可以游玩:「受虐癖测试」- MasoCheck

这两个示例大致展现了Ink制作互动文游的特点。当然,Ink这个语言潜力很大,可以做出比这好得多的东西,至少是比蓝枫做的好得多。


环境搭建

这其实很难说是什么……环境搭建之类的东西。就,很容易,下载安装几个工具就可以了。


Inky

Inky是Ink的主要开发工具,它可以提供即时的测试。

可以直接在Ink的官网下载:ink - inkle’s narrative scripting language

选择自己对应的操作系统,下载解压即可,它是绿色的。


Git

建议是安装一个Git,方便部署。

在Git的官网就可以下载:Git

注册个GitHub账号,如果没有的话。


VSCode

建议通过VSCode来编写Ink的代码,因为Ink本身的这个编辑器……在我看来……有点,反人类(

在VSCode的官网下载:code.visualstudio.com

然后装个插件:Ink。装好了就有代码高亮。

VSCode写起来比Ink编辑器要舒服得多,要测试的话直接复制粘贴到Inky里面,再保存一下就可以了。

当然如果不介意Ink这个代码编辑器的话,也可以直接在上面写(


UI简介

不打算详细介绍UI,稍微讲一下。

这就是Inky的UI。左边可以编写代码,Ctrl+S保存以后右边的界面同步更新,右边可以直接体验和测试自己写出来的东西。

右上角的回退按钮,左边这个单箭头是回退1步,右边这个双箭头是直接回退到起点。

Files可以新建、打开、导出。

基本就这样。


语法

Ink的语法并不复杂,这里只介绍一些最基本的部分。


文本(Text)

非常基本的元素,直接打字。

1
你是一个冒险者,你进入了一片魔法森林。

没有什么好说的,文本会直接显示在故事中。


结点(Knots)

结点是Ink的核心概念之一,一个Ink故事基本就是由结点组成的。

结点分为三个层次——结点(Knots)针脚(Stitch)标签(Label)


结点(Knots)

在Ink中,结点以===表示:

1
=== Knot1 ===

它是组织故事结构的基本单元,也可以理解为跳转点。


针脚(Stitch)

在Ink中,针脚以==表示:

1
== Stitch1 ==

它是结点内部的细分,可以用于细化故事的内容,分清层级结构。但其实,在结点显示器上,它是和结点平行的。

这就很怪,没错,这好像是一个遗留问题还是什么的……?

总之,在自己写代码的时候,最好就是和自己的风格保持一致。当然随随便便混淆也是可以的,影响不大。


标签(Label)

在Ink中,标签以=表示,标签名后面没有=

1
= Label

标签是结点或针脚内部的细分,这确实是能够划出层级结构来的。它也可以用来跳转,问题不大。


可以在菜单中单击Toggle knot browser,左侧会显示故事的层级结构。

类似于这样:


转移(Divert)

转移(Divert)可以理解为针对结点的goto,我认为,学过一点程序设计的应该能够很快理解。

总之它的功能就是,故事跳转到对应名称的结点/针脚/标签处。

1
2
3
4
5
6
	故事开始
-> forest

=== forest ===
你是一名冒险者,你进入了一片森林
-> END

例子已经很明显,不多赘述。

需要注意一下的是,**-> END-> DONE**会导致故事的结束。

在故事可达的所有分支,都要写上这一句,否则会导致ran out of content的情况出现。自己测试的时候注意。


选择(Choice)

选择(Choice)会暂停故事的推进,并让玩家进行选择交互。它通常以+表示,也可以用*表示。这其实有一些不同,但我的建议是,全部用+

如果说为了避免出现重复什么的,才用*,一个一次性选择。


普通选择

先讲一下普通的选择吧,这是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
	故事开始
-> forest

=== forest ===
你是一名冒险者,你进入了一片森林。
你来到了一个岔路口,左边是一条碎石路,似乎通向一个村庄。
右边是一条林间小路,通向森林深处。

+ [向左走,尝试靠近村庄] -> villageEnd
+ [向右走,尝试深入森林] -> succubusEnd

=== villageEnd ===
你成功进入了村庄,村民欢迎了你,你安全了。
你住进了村民的家里,结果你半夜醒来,发现自己被人家家里的少女夜袭了,于是你果断地缴械投降了。
-> END

=== succubusEnd ===
你决定深入森林,结果遇到了一只魅魔。
她的等级比你低,你明明能打过,但是你因为自己的败北癖和色欲输掉了,于是你被魅魔榨得一滴不剩了。
-> END

一次性选择

讲讲一次性选择,这也是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
故事开始
-> table

=== table ===
你是一个冒险者。
你被一只魅魔抓了起来,现在你的面前是一张桌子,上面摆着三瓶药剂,颜色不同。
魅魔一脸坏笑地看着你,显然,你是无论如何也是只能把这三瓶药剂喝下去的了。
「硬撑着不喝的话,你知道会发生什么的,对吧?」
VAR potionNum = 3
VAR potionCount = 0
+ [我,莫得选择] -> drink

=== drink ===
现在有{potionNum - potionCount}瓶药剂摆在你面前。

{ potionCount < potionNum:
现在有{potionNum - potionCount}瓶药剂摆在你面前。
* [喝下红色的药剂] -> redPotion
* [喝下紫色的药剂] -> purplePotion
* [喝下蓝色的药剂] -> bluePotion
}
{ potionCount == potionNum:
药剂都喝完了。
+ [喝完了]-> succubusEnd
}

=== redPotion ===
你喝下了红色的药剂,味道像是树莓/草莓果汁,甜的,很好喝。
然而没过多久,你的身体开始发热,全身都变得非常敏感。
魅魔轻轻在你耳边吹了一口气,你差点瘫软下去。
~ potionCount++
+ [喝完了] -> drink

=== purplePotion ===
你喝下了紫色的药剂,味道像是葡萄/黑加仑混合果汁,酸甜的。
然而没过多久,你感到身体有些异样,下体有些肿胀。
魅魔轻轻碰了一下你的肉棒,一股紫黑色的黏液从马眼漏了出来。
~ potionCount++
+ [喝完了] -> drink

=== bluePotion ===
你喝下了蓝色的药剂,味道像是葡萄糖酸锌口服液,还不错。
然而没过多久,你感到思维有些放缓,同时莫名感到有些腹胀。
魅魔舔了一下你的耳朵,你感到几乎已经无法思考了。
~ potionCount++
+ [喝完了] -> drink

=== succubusEnd ===
你在药剂催情、胶液寄生、轻度人格排泄的情况下被魅魔吃干抹净了。
你变成了魅魔的小精奴,每天和魅魔小姐过着性福快乐的生活。
-> END

这两个例子大概很不正经(

这个例子涉及到了一些变量和分支的内容。后面会提一下。

但总之,读者可以复制粘贴自己试一试,或者自己写一个故事体验一下。Inky的测试运行很方便。


变量(Variable)

为了省事,这里就只讲全局变量。反正故事一般也不会太复杂(复杂的故事大概就不是用Ink做了)。


定义变量

定义变量很容易,比如说:

1
VAR potionNum = 3

有一点点编程基础就能看懂了。这里定义了一个名为potionNum的变量,它的值是3。

变量的定义是块作用域,这个听不懂没关系,你直接把所有变量定义写在最前面,别写进大括号里,就行了。


修改变量

很容易,和C++差不多。

就是注意,在行首写上~

例如赋值:

1
~ potionNum = 3

运算:

1
2
~ potionCount += 1
~ potionCount++

条件分支(If)

为了省事,只讲一点点条件分支。


条件:玩家的选择

可以根据玩家已经做出的选择来进行分支语句的编写,例如:

1
2
3
{	redPotion:
~ statusEstrus = true
}

条件:变量和表达式

可以根据变量和表达式来编写分支语句,例如:

1
2
3
{	potionCount == 2:
还有两瓶。
}

部署到Web

写完Ink故事之后,在File-导出到Web选项,选定一个目录,可以导出一个文件夹。里面通常是包括这些文件:

准备好自己的文件结构,在这个目录下使用Git,推送到GitHub的远程仓库,然后部署到GitHub Pages上就可以了。

这一步不详细展开,网上教程很多。


结语

这篇文章写得有点草率,后半部分突然不太想写了,所以写得很粗略。

总之,简单介绍了一下如何使用Inky写一个Ink故事。

后续如果想起来的话可能会尝试完善一下。

如果你觉得文章写得还不错,可以赏作者一杯咖啡喝,或者一顿饭吃。感谢支持!THYzrcoMQf7d7wzGu1PvDraTef87abSv9V