谈谈 AVG 游戏的程序脚本(上)

谈谈 AVG 游戏的程序脚本(上)

其实这是一篇灌水文。

今天看了一天动画感觉人都废了(幸好还把毕业论文提纲写完了),现在距离我睡得着的睡觉时间还有一阵,随便写点刷刷存在感好了(跑

几种脚本

命令式的脚本

类似于命令行或 Shell 的语法,基本结构是 命令名 参数1, 参数2, ..., 参数N,代表是 NScripter,下面是它的程序脚本的一段:

*define
clickstr "。",1
game
*start
你好,世界。
effect 3,15,2000,"m3.bmp"
这是我第一次写NScripter脚本。
end

这里要提一下吉里吉里2(aka kirikiri2 or krkr2),它的 KAG 脚本看上去是这样的:

[wait time=200]
[loadplugin module=wuvorbis.dll]
*start
[startanchor]
[cm]
[rclick enabled=false]
[clickskip enabled=false]
[history output=false enabled=false]

看上去像是标记语言,但实际上它仍然是命令式的语法,至于为什么应该也不难理解:它并没有「声明」什么东西,不过是命令行脚本换了个格式而已。但值得一提的是,这样的设计对人明显更加友好。

对于 AVG 来说,命令式的语法本是一种十分恰当的设计,不像 RPG 等其他类型, AVG 总是有着一套固定的剧情流程的,哪怕有再多的分支也仍然是有限个故事线。命令式语法天然能够清楚地描述「流程」,并且足够通俗易懂。

但这仅仅是对于剧情脚本来说的,NScripter 将全部引擎功能都放到命令行式的语法中,随之而来的是系统功能编写的复杂化。这些脚本不得不引入了程序语言中的分支、循环语句甚至该死的GOTO语句。当然,如果只是要做一个常见的标题界面、设置界面或是音乐鉴赏,这可能并不算是困难,顶多是麻烦。那么,来试试做一个滚动条?处理一下各种鼠标键盘事件?不过,这些对于熟练工来说也算不了什么,那么,做个解谜游戏呢?

例如,一个简单的文字历史界面脚本大概是这样的:

(不管你懵没懵,反正我懵了……)

KAG 中引入了 [if][endif] 等结构化语句来解决逻辑问题,但这也使得脚本的命令式特性不再纯粹,换言之,每行脚本的独立性被破坏,取而代之的是以顺序排列或嵌套的「程序段」。大幅增加了学习成本。

或许这还不是最糟糕的情况。更糟糕的是你用它编写系统时的复杂度,如果你不了解这些脚本没有概念,那么想象一下用 Shell 脚本在控制台里写一个模拟窗口,或是用原生DOM写一个 SPA,或是用 SDL 造一个窗口。实际情况在事件处理方面或许比这些类比例子要好不少,但感受上可能只有程度的区别。维护的难度也可见一斑,常常听到的事情是,某个制作组的程序因故退出,后补的新程序员看了看前人写的代码,大肆吐槽一番,然后大幅重构了它……

而 NScripter,你甚至要自己造一个死循环并配合Goto语句来处理按钮点击事件!

引入更像程序语言的脚本

事实上,KAG 不过是吉里吉里2的二等公民,其真正的脚本系统是名为 TJS 的脚本语言。而 NScripter 也终于在其问世10年后(2009年)提供了 lua 脚本支持。

TJS 是其作者发明的专为吉里吉里2使用的语言,无论是名字还是长相都很像是 Javascript,不过,考虑到其发布年代,真相可能更接近于是 ActionScript2 的仿制品(反正是 ECMAScript 就对了)。语言特性在当时来看还是很不错的,更重要的是能直接操作底层的图形对象。这让它更像是一个通用的游戏引擎,而非是文字游戏引擎,这也是为什么后来有人用吉里吉里2造出了其他功能更强的剧本系统甚至 RPG 系统。

不过令人失望的是,大概是因为太过底层了吧,反而学和用的人更少了。更多的人只拿它实现一些 KAG 难以实现的算法函数供在 KAG 中调用或在必须调用 TJS API 时不得不用,大部分系统功能仍然是靠 KAG 实现。

试试混写?

既然大家都喜欢命令式语法,有时却又不得不写逻辑,导致命令式语法的优雅被破坏,有没有将二者作出区分的方法?BKEngine 的脚本系统或许是个例子。

BKEngine 的脚本系统直接参考了 NScripter 和 TJS/KAG,最后的结果是一个形式上与 TJS/KAG 极为相似,但又明显不同的语法:

  • BKSCR/BKPSR(BKPSR 后改名 Bagel)组成二元结构,分别对应 KAG/TJS
  • 与吉里吉里2不同,二者都是一等公民,并且支持混写

所谓混写看上去是这样的:

*start
[sprite index=100 file='demo.jpg']
#if x > 1
[addto index100 target=basic_layer x=0 y=0]
#else
[addto index100 target=10 x=0 y=0]
#endif
[bgm file='music.ogg']

虽然在学习 KAG 的过程中仍然保留了[if] 等语句,但明显上面的写法看上去更清楚,流程控制与流程内容明显地区分开了。

虽然是 BKEngine 是自家产品,但就算我刻意不去说它的缺点,它的缺点也仍旧是显而易见的:这是一种治标不治本的方法,也依旧无法避免系统编写和维护的困难。

=====

啊,时间有点晚了,那剩下的内容就下篇再见吧

知乎这编辑器我也是没脾气了,排版很糟糕,想看舒服的版本可以等我全部写完,会发完整版到奶牛关~

(下篇我要开始吹 AVG.js 了,大家准备好喷我!)

编辑于 2017-04-09

文章被以下专栏收录