[!CAUTION]

该教学风格主要针对那些对代码层面完全零基础的受众群体,故文章会介绍很多基础的知识,请根据自己的知识范围对其内容进行甄选查看。

该教程不作为最终版本,后续将会进行更改,如有问题可在评论区提出

一、改版前的准备

1.什么是TyranoScript

  关于本文章,与其说是针对希尔薇改版所准备的教程,不如是“在借用了希尔薇游戏代码为例子的情况下,教你如何使用TyranoScript”的教程,也就是说该教程本质上是在教你如何用TyranoScript制作一个GalGame。

  现在让我们言归正传,什么是TyranoScript?TyranoScript是一款基于 HTML5 和 JavaScript,能够制作出在NW.js框架或Electron框架上运行的游戏的游戏制作引擎,我们能够通过简洁、易懂的代码,制作一款属于自己的游戏,还可以通过其他开发者自制的插件,实现TyranoScript引擎本身不存在的功能。而《Teaching Feeling》(奴隶少女希尔薇)便是基于该引擎所制作的游戏。

  其中,该游戏的1.0到2.5.2版本,使用的是NW.js框架用于运行游戏,可以使用Tyrano官方的Tyrano Rider对游戏进行辅助修改。

  而游戏的3.0到4.0.6版本,则使用了Electron框架用于运行游戏,可以使用Tyrano官方的Tyrano Script V5对游戏进行辅助修改。

  这里对两个框架的区别和作用不做过多介绍,只是稍作了解。


2.文本编辑器的选择

  就和C++、Java或是Python那些代码语言一样,我们也是需要一款文本编辑器来对代码进行修改,这里针对电脑Windows端和手机安卓端各推荐一款编辑器

  电脑端推荐-Visual Studio Code

  也就是我们俗称的VSC,该编辑器的优点在于拥有诸多的功能以及优秀的插件,其中Tyrano官方提供了VSC的语法高亮插件,使得我们能够更直白明了的查看游戏的代码。

  手机端推荐-MT管理器

  手机端其实很遗憾的没有带有语法高亮的编辑器,只能使用原始的黑白文本,但这里还是推荐了MT管理器,原因是它针对文件管理相比较于其他管理器拥有更多的功能,并且采用双列文件管理,能够更方便我们对改版文件进行管理与更改。

下图中的图片左侧为未启用语法高亮的代码片段,右侧为启用了语法高亮的代码片段


3.了解文件结构

  接下来,你需要了解游戏的文件目录结构,也就是存放游戏数据的data文件夹内的东西。

  其中,NW.js的data文件夹就在根目录,而Electron的data文件夹则存放于“.\resources\app”(此处的\代表目录分隔符,“.”代表游戏根目录,也就是说整体意思为游戏根目录下的resources文件夹内的app文件夹内)

  如下图所示,左边为NW.js,右边为Electron


  当你打开data文件夹,你则会看到以下文件夹,而他们的作用分别是

    bgimage——存放游戏背景图片

    bgm——存放游戏背景音乐

    fgimage——存放游戏内容图片(包括角色所有立绘以及衣服)

    image——存放游戏按键图片(包括对话按键,菜单按键在内的所有图片)

    scenario——存放游戏剧本文件(包括游戏文本以及变量)

    sound——存放游戏音效文件(包括做爱时的精液声)

    system——存放游戏系统配置文件(包括字体类型大小、快进速度等近100项游戏外的设置)

    video——存放游戏视频文件(包括游戏开头logo)

  (另外在部分版本当中可能还存在scenario_old、scenariod_beat,这两个文件夹是作者Ray-Kbys对旧版本游戏的备份,完全无用,只是忘记删了。其余文件需要根据当前版本考虑作用,有些作者可能会乱塞东西。)


  对于以上这些文件夹的使用,取决于作者要怎么制作自己的改版,如果有人要制作一个新的开头动画,那文件需要放在video文件夹内,如果需要加入新的菜单按钮之类的,也更建议放到image文件夹内。这些分类一定程度上并不是固定的,虽然你也可以把按键放到fgimage文件夹内,但是更建议遵守规则来放置图片,因为图片的调用在系统中是按照默认文件夹来调用的,比如你想要调用一张按键图片,那系统会默认从image文件夹里面找,背景图和其他文件也是同理,会在对应文件夹去找,除非你在调用按键的时候额外写一个参数告诉系统你要从指定的文件夹里面去找图片,那样未免太过于麻烦了。


二、改版制作教程

1.文件要素

  讲了那么多,现在已经可以开始正式进入教程了。

  首先,我们需要知道,在TyranoScript的游戏脚本文件中,主要由两种的东西构成,也就是文本(Text)和标签(Tag),至于标记(Label)和名称标记(#),本质上是辅助代码用的,名称标记更只是个附属用品,这个后文会讲到。(那个名称标记#其实并没有名字,这是我取的,所以英文名就写了它本身)

  那文本和标签具体是什么东西,可以让我们来看一段代码

1
2
3
4
5
6
7
8
9
10
再次初次见面、我叫希尔薇。[lr]
感谢你收留我。[p]
虽然无法干力气活、但我想如果你让我错简单的杂用程度的活我可以做到。[p]
[chara_mod name="body" time="100" storage="s/body/stand-c.png" ]
不过、以前的主人说过“以听我的惨叫为消遣是最有价值使用方法”。[p]
[chara_mod name="body" time="100" storage="s/body/stand-t.png" ]
…求你、手下留情。[p]
[stop_bgm][black][chara_stop]

[jump storage="intro/step1.ks" target="*step1" ]

前面说的语法高亮只是由插件做成的第三方效果,TyranoScript本身在系统里没有对应的默认原生语法高亮,此处套用了css的语法高亮

  在上述代码中,你能够看到两样东西,分别是普通的文字文本,和带有括号的标签。

1.1 文本

  TyranoScript的文本就像小说一样,是不需要像其他游戏引擎或者程序语言那样,用任何代码或标识符将文本单独输出,比如游戏引擎Ren’py需要使用双引号(”“)来输出对话。

  因为代码被[]框住成为了标签,所以不用担心系统会把文本和代码搞混。

  也就是说,你只需要把文本输入进去,然后系统就能够读取到,并且显示在预设的聊天框内。

1.2 标签(重点)

  在上述代码中,你能看到诸如[lr]、[p]、[chara_mod name=”body” time=”100” storage=”s/body/stand-c.png” ]、[stop_bgm]等标签。也许此时会疑惑,为什么又长又短,而这正是参数的不同造成的。

  标签由两部分组成,分别是名称(Name)和参数(Parameter)

  当中,名称本质上是一个(Macro)的名称(这个东西下文会提到,你可以暂时不用管这个),用于增加可读性,比如上面有一个标签叫[stop_bgm],它的名称就是stop_bgm,你一眼就可以看出来它是负责停止bgm播放的一个标签,但实际上也是如此,它的作用就是停止bgm的播放。

  而且所有标签的名称永远是[]内的第一位

  不同于官方的标签,由你自己定义的标签的名字是可以随意更改的,但是要记住,标签的命名尽量使用英文。空格用_代替。

  而参数比较特殊,它是对标签的一个补充,就比如上述标签中的

1
[chara_mod name="body" time="100" storage="s/body/stand-c.png" ]

  它由名称和参数两个部分组成,待在第一位的chara_mod就是它的名称,而后面的name、time、storage则是他的参数。这个标签的作用就是更改角色的图像,通常会用来更改角色的表情,但在此处用来更改整个角色立绘。

  这里我们倒着讲,最后一个storage参数的作用就是定义你要调用什么图片,在上面,它调用了fgimage文件夹里面,一个名称为s的文件夹内,名称为body的文件夹内的stand-c.png图片。(至于为什么是fgimage可以翻回去看一下文件结构那部分),所以在这里写作s/body/stand-c.png,后续不再做过多的赘述,直接用xxx/xxx/xxx的形式表示文件结构。

  time参数的作用在此处的作用是定义图片切换的淡出淡入时间,单位为毫秒(ms),此处定义为了100,则代表着切换角色图片时会有0.1s的淡出淡入,如果设置为0的话则直接变换,不做淡出淡入。

  对于name参数,它作用就是用于管理同一name参数的角色图片。(实际上它并不是由chara_mod定义的,而是由另一个叫做chara_new的标签定义的,只不过我没在这里写出来。)

  比如上述代码中

1
2
3
[chara_mod name="body" time="100" storage="s/body/stand-c.png" ]
不过、以前的主人说过“以听我的惨叫为消遣是最有价值使用方法”。[p]
[chara_mod name="body" time="100" storage="s/body/stand-t.png" ]

  两个chara_mod标签分别调用了叫stand-c和stang-t的图片,但是它们的name都是boay。要注意,此处的name并不是指它们的文件名称叫做body,而是指它们在系统中都属于“boay”这一名称的分类。而且游戏中不能同时存在两个name一样的图片,则是会被切换(time的切换淡出淡入就是在这里使用的),stand-t.png被调用后,由于场上已经存在name为body的图片了,所以stand-c.png就会消失在游戏里,stand-t.png则会出现在游戏中。

  当name为其他值的时候也是同样的道理,游戏里只能存在同一个name,后调用者会替换前调用者。但是chara_mod能调用哪些name,需要看chara_new赋予了什么样的name,如果说chara_new赋予三张图片的name分别是eye、body、hand,那你如果用chara_mod是调用出name为foot或是其他值的图片,否则游戏将会报错,因为chara_new并没有赋予这些不存在的值。

  而且务必要记住,参数后面的值要跟着半角上下引号(也就是英文输入法内的上下引号)。虽然一般情况下也可以不写,但是部分参数不写的话会报错,所以为了不用专门记哪些参数要加上下引号,这边建议是统一写上下引号

下图为游戏内stand-c和stand-t替换的过程

IWTh.gif

1.2.1 基础标签

  接下来将会罗列几个基础的标签和他们对应的作用,以此便于你们能够做出最基本的文本

标签 作用 用途
[r] 文本换行
[l] 停顿文本,单击屏幕后继续
[lr] 停顿文本,单击屏幕后换行
[p] 文本切页(这么讲便于理解)
[s] 暂停代码运行 该标签后的代码不会运行,可以防止不同作用的代码混起来
[return_bace] 返回行动菜单 行动菜单即带有摸头,对话按键的那个界面
[black] 清除游戏背景
[cm] 清空游戏文本

1.3 变量(重点)

  因为游戏的底层逻辑是基于标签以及变量组成的(不如说所有编程语言都差不多),所以这部分和标签一样,非常的重要。

1.3.1 什么是变量

  首先,我们来理解,什么是 变量 (Variable)

  在原版游戏2.5.2中,当希尔薇对玩家的好感度低于50的时候,那么希尔薇就会在生病事件中死去,如果等于或大于50,则会活下来,开启后续事件。

  在这段事件中,“好感度”就是一个变量,用来判断玩家是否满足一个特定的条件。变量本身是游戏程序的保存下来的不同数值,就比如刚刚说的“好感度”,我们可以通过特定的代码更改这些变量的值。

  使用一个比较形象的比喻的话,那就是变量相当于一个盒子,每个盒子都拥有着自己的名字,盒子里面装的就是一个个数据,他们可以是字符串(也就是普通的文本),可以是数字,也可以是其他东西。而游戏程序就是盒子外的人,根据指定的条件,来判断需不需要对某个盒子进行更改。

  也就是说,当一个叫做”好感度“的盒子里面的数字大于或者等于50的时候,盒子外的人才会决定让希尔薇活下去,否则希尔薇就会死亡。

  这就是变量在程序中最基本的运用。

1.3.2 变量的命名

  变量是有着属于自己的命名方式的,你不可能在代码里面真的将变量命名为“好感度”,这种是我们自己的命名,对于游戏程序,我们还是要给变量取一个程序看得懂的名字。

  关于变量的命名,基本和所有编程语言一样,TyranoScript的变量命名也遵循一下几种规则

  1.请使用数字,英文字母和下划线(_)命名

  **2.请勿使用空格,如果想使用分单词,可以使用驼峰式命名(**如MacCom,这种单词的首字母大写),或使用下划线(_)代替空格(如Mac_Com)

  3.请勿使用数字开头(如1MacCom)

  但是作为JavaScript为基础代码的游戏,理论上命名也可以使用中日韩等字符,但是不建议这么写,另外在如果有部分变量在设置后就再也不会变化,建议使用全大写命名,便于分辨(如MACCOM)。

  另外在TyranoScript中,变量分为两个部分,也就是种类名称,必须要遵循“种类.名称”式的命名法,例如变量“f.love”,f就是他的种类,love才是他的名字,中间必须用”**.**“来隔开。

  给变量取一个易懂的名字,可以很大概率提升你后续开发或是别人使用你代码时的效率,比如你想把一个变量用来当做游戏所度过的天数,那你就可以给这个变量命名为f.day或者f.tianshu,前者day就是天数、日子的英文,后者tianshu就是天数的拼音,这样你一眼就能看出来这个变量在游戏里有什么用,别人也可以。否则你给这个变量取一个名字叫做f.a114514,你第一眼也看不出这变量到底是干什么用的,别人也只能根据这个变量在代码里的作用猜测它到底是做什么的,实在是太过费时费力

  在TyranoScript中,所有的初始变量都写在了scenario的exp.ks文件中,便于管理,虽然说并不一样需要都写在exp.ks文件里,你写在其他地方一样赋值,就算写在游戏剧情里也可以,因为变量的设置只是后台的人操作,玩家看不见,但是便于管理,如果没有特殊需要的话,通常变量都是写在exp.ks文件中提前赋值,不然你把10个变量写在10不同的文件里,等你想找的时候你又不知道从何找起。(具体exp.ks文件位置在不同的游戏版本是不一定一样的,需要你自己找一下)


1.3.3 变量的种类

  在TyranoScript中,变量有着三种类型,分别是普通变量全局变量临时变量三种,可以查看下列表格来更明确他们的作用(列表中的例子变量是随便取的名字)

种类名称 种类 种类作用 例子
f 普通变量 游戏内最常见最基础的变量,每个存档的普通变量都不一定一样 f.love f.name f.money
tf 临时变量 无论给这种变量取什么名字赋什么值,所有这种变量在游戏程序被关闭后一定会被消除,不会永久保存 tf.time tf.name tf.apple
sf 全局变量 这种变量是全局的,所有存档共用一个变量。也就是说你在存档a设置了一个全局变量为1,那么存档b的这个变量也一样会被更改为1,就算重新开一个存档,这个变量还是1,除非重新赋值 sf.hcg sf.hcg2

1.3.4 变量的赋值

  我们可以来看一下以下代码

1
2
[eval exp="f.love=0" ][eval exp="f.lust=0" ]
[eval exp="f.name='主人'" ][eval exp="f.name_h=0" ]

  其中,eval标签的作用就是给变量赋值,而参数exp就是具体的赋值表现,上述代码块中,存在四个普通变量,f.love变量(好感度)和f.lust变量(淫乱值)被赋予了0作为初始变量,这也是游戏一开始希尔薇没有任何好感度和淫乱值的原因。而f.name(玩家称呼)则被默认为了“主人”,所有游戏里的希尔薇如果在不更改称呼的情况下,就默认称呼玩家为主人。而最后一个f.name_h(玩家淫语称呼)则是和希尔薇在做爱的时候,希尔薇称呼玩家的特殊淫语,该变量在后期需要单独设置,所以这里被设置为0来表示不作任何值。

  而具体赋值语法则是[eval exp=“变量赋值”]

  其中“变量赋值”可以写最基本的赋值语句,也可以使用加减乘除进行最基本的更改

1
2
3
4
5
6
7
8
9
10
11
12
13
[eval exp="f.love=0" ]——将f.love直接设置为0

[eval exp="f.love=f.love+3" ]——将f.love值加3

[eval exp="f.love=f.love-3" ]——将f.love值减3

[eval exp="f.love=f.love*3" ]——将f.love值乘3

[eval exp="f.love=f.love/3" ]——将f.love值除3

[eval exp="f.love='好感度爆棚1'" ]——将f.love值设置为字符串“好感度爆棚1”(设置字符串时要给外面加上单引号,不加单引号只能设置为数字)

[eval exp="f.love=f.day/3" ]——将f.love的值设置为当前f.day的值的3

  以上是最基本的赋值语句,你也可以同时给多个变量赋值,但是需要用半角逗号(不能使用中文输入法的逗号)隔开比如

1
[eval exp="f.love=f.love/3,f.day=f.day+1,tf.awa='awawawwaa'" ]

  上面说的只是最基本的赋值语句类型,对于目前来说已经足够用了,更多的赋值类型如有需要再说

1.4 其他

1.4.1 标记(重点)

  让我们假设,在一个文件里,写着有七八种实现不同功能的代码,那我们该如何精准的调用自己所需要的功能呢?你也许会说把它们写到不同的文件以此来调用,但是这样未免过于麻烦了。所以,这个时候,我们就需要用到标记

 首先,关于这一部分我们依旧需要讨论几个老生常谈的问题,即:

   到底什么是标签?
   标签有什么用?
   如何使用标签?

  标记(Label),在游戏中是对代码块的区分起到标识的作用。在文件中,标签都以*星号()**作为开头。

  按照惯例,我们依旧结合代码来讲解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
*step1
[set_sit][f/a_r_nt][show_sit][show_repeat_role_room][bgm_SG][_](啊、怎么了吗…。[p_][jump target="*before_menu" ]
——————
*show_bace
[cm][clearstack][set_sit][f/a_r_nt][set_time_r][show_sit][show_repeat_role_room]
[if exp="f.act=='non'" ][eval exp="f.act=0" ][bgm_SG][syl][f/cl]…早上好、主人。[p_][endif]
[_]
——————
*before_menu
[if exp="f.act>=7" ][jump target="*night" ][endif]
[set_time_r][show_button_intro]
——————
*night
[eval exp="f.last_act='awake',f.act='non',f.day=f.day+1,f.repeat_mode=0" ][stop_bgm]
[if exp="f.intro_flag==0" ][jump storage="intro/event.ks" target="*feed" ][endif]
[if exp="f.day>=4" ][jump storage="intro/event.ks" target="*to_next" ][endif]

[f/a_r_nt][_]…。[p_](已经到晚上了…准备睡觉吧。[p_]
[syl][f/r]…晚安。[p_][_][black]…。[p_]
[jump target="*show_bace" ]

  上述代码是从同一个文件里拿出来的。从内容来看,你能够看到4个以*开头的单词,它们就是标记,用于给不同的代码块命名,方便调用,在这里,它们的作用各不相同,你现在不需要知道里面的语句到底是干什么的,你只需要知道它们的大致作用就可以了。而且它们还划分出了四个不同的代码块。(“——————”在源代码中并不存在,这里是为了方便明确不同的代码块而手动添加的。)

   step1————用于在开局收养完希尔薇后增加一个过渡对话。
   show_bace————用于开始新的一天
   before_menu————在你用行动菜单进行摸头对话等操作后判断是不是晚上了,如果是则转跳到night,如果不是则将推进一格时间并且重新显示行动菜单(因为对话会清除行动菜单的按键)
   night————在条件符合时触发游戏初期的吃饭睡觉等事件,如果无事件触发则正常触发夜晚对话,然后转跳到show_bace开启新的一天

  首先我们要知道,系统是从文件第一行开始,逐行往下阅读代码,直到最后一行,这个过程是不可逆的,不可能先阅读1、3、5、7、9行,再阅读2、4、6、8、10行这样子。而标记就给系统提供一个便捷的中间调用的功能。

  就比如你调用了上述的代码文件,那么它只会从*step1开始逐行往下调用,但是假设你需要从*night开始调用呢?标记就把这部分标记了出来,告诉系统可以从这里开始调用。

  如果没有标记,那么上述四个功能就会变为一体,你无法单独调用其中任何一个功能,每次调用都要从头开始,而有了标记后,他们四个功能独立了,就变得可以单独调用代码块了。

  而且标记除了分隔代码块以外,还起到和变量名称一样的说明作用,像*night一眼就能看出来是和夜晚有关的代码块,但是命名和变量名不同的是,你可以使用中文或日文等其他语言,比如“*夜晚调用”、“*开启下一天”之类的,但是为了美观,通常还是建议使用英文。

  所以标记让游戏代码读取变得更加高效,在没有需要额外调用代码情况下,你可以将不同的代码块按顺序放在一起,只要他们的顺序能达成你的目的。如果需要额外调用,那再使用调用语句就可以了。


  但是也有人会问,那我们应该如何阻止阅读完上一个代码块后,系统继续向下一个代码块运行呢?

  在这里,我们有两种方法:

  1.使用jump标签

   jump标签的作用就是跳转到对应文件或者对应标记,它有两个参数

参数名称 参数作用 例子
storage 指定跳转文件,如为空则使用本文件 [jump storage=”intro/opening.ks”] ——跳转到intro/opening.ks文件的开头开始读取代码
target 指定跳转标记,如为空则从文件开头读取 [jump target=”*opening”] ——跳转到本文件的*opening标记开始读取代码
[jump target=”*no” storage=”intro/opening.ks”] ——跳转到inrto/opening.ks的*no标记处开始读取代码

   注意,这两个参数必须至少存在一个,不可以两个参数都不写

  2.使用[s]参数

   具体可以看前文1.2.1 基础标签一章中[s]的部分,它可以使代码运行到它这里就完全不再继续往下阅读,这通常用在代码块的结尾处,但务必记住在使用[s]的时候要有调用出去的方法,否则如果你在游戏对话的时候使用了这个,那你对话就会完全卡住无法运行。前文的before_menu之所以可以阻止代码的运行,是因为[show_button_intro]标签宏内带有[s],如果没有[s]的话,就会自动运行到*night。