C156 糖水俱乐部 – 5 月开发日志

,

4,764字

,大约需要

20–30 分钟

《C156 糖水俱乐部》的开发工作是从四月底开始的,但是整个四月期间基本是在做准备工作。这些准备工作包括制作暂时的占位素材、策划游戏的玩法和剧情、实现一些程序上的基础设施等等。

来到五月,我刚考完期中考试,挑战杯的事情暂时结束,而且还遇到了五一假期,五月是我最闲的一个月,所以开发进度很快。

有一说一,我不太会写开发日志没,我会把这个过程写得很无聊,像一篇产品说明书。B 站一些 UP 可以把游戏开发的过程拍得很有意思,很厉害。当你开始持续做一件事情的时候,肯定是会无聊的。构思的过程比实现要简单的多,对于游戏开发来说,实现的过程尤其是挺痛苦的😖。

功能开发进展

在整个五月,最重要的进度是完整地实现了对话和调酒系统,对话与调酒是游戏的核心玩法。

虽然 Godot 目前有功能很完善的的对话插件,但我还是选择自己开发一个对话系统。

这样做的结果就是,DialogueParser 几乎没有多余的功能,它一共只有 500 多行的体量,却实现了所有必要的功能,并且原生支持调酒系统。

DialogueParser 可以读取并解析 .txt 扩展名的纯文本格式对话脚本,然后在游戏中通过一行简单的命令 DP.play("filepath") 来播放这段对话(也可以从某个特定标签开始,若不传第二个参数则默认从头开始)。

对话脚本的语法很简单,参考了 Ren’Py 的 labeljump 语法,可以在标签之间跳转:

GDScript
# 首先要对所有角色进行注册
define character Alex
portrait "res://entities/gameplay/counter/characters/alex.tscn"
animations default,happy
theme_color "#8DEBFF"
voice "res://assets/audio/alex_pop.ogg"
end

label start
Luca >> 晚上好。
Alex >> 晚上好。
Alex:happy >> 现在我在笑😄。

jump next_part

在对话脚本的开头,首先要对角色的立绘、动画、主题色等信息进行注册,这样在之后编写对话的时候就可以直接使用了。这一部分也可以单独保存成一个纯文本文件,之后在对话脚本的开头就可以用“import + 路径”的方式重复使用角色数据了。

角色立绘的显示、隐藏都可以用一行脚本方便地控制。并且,这些脚本有对应的内联形式,可以在一句对白播放的中途执行。

GDScript
show Alex at 946,698
play Alex default
hide Alex

Alex >> show(Alex,946,698) >> play(Alex,happy) >> 我来了。
Alex >> hide(Alex)

丰富的内联脚本也是我使用 >> 进行隔断,而不是只在名字后面加一个冒号的原因。使用 >> 可以把一句话分成多段,并在对话中插入需要运行的函数。并且 >> 在正常对话中出现的概率很小,如果确实有这个需要,可以使用转义字符 \>\> 来在对话中显示 >> (应该没有这种需求吧)。

还可以使用 await 等待一段时间,如果参数为 0,则等待玩家的输入。

GDScript
Luca >> 前半句, >> await(0.5) >> 后半句。
Luca >> 点击之后才会显示后半句, >> await(0) >> 这是后半句。

和其他所有的对话系统一样,DialogueParser 也支持分支选项,通过 choice 就可以触发:

GDScript
choice
- 这是选项一! >> choice1
- 这是选项二! >> choice2

label choice1
Alex >> 你选了选项一。
jump continue

label choice2
Alex >> 你选了选项二。
jump continue

label continue
Luca >> 继续吧。

当遇到 choice 指令时,选项 UI 就会自动触发。我月底的时候还给所有的选项添加了辉光效果,虽然图片无法表现出来,不过实际效果还是不错的。这是一个“GlowButton”类,自带一个脚本用于实现这个效果,用来替代所有的普通 Button 类节点。希望 Demo 发布之后大家可以来体验。

并且支持根据条件进入分支:

GDScript
switch boolean
case true >> jump label1
case false >> jump label2

此外,DialogueParser 支持进行相机控制。因为《糖水俱乐部》使用了视差背景效果,在 2D 世界中通过视差模拟空间感,所以为了实现一些演出效果,需要在对话过程中控制相机的位置和缩放 (zoom)。

GDScript
camera move 960,540
camera move 960,540 0.8

camera zoom 1.2
camera zoom 0.8 0.6

# 也可以使用复合的指令
camera move to 960,540 with zoom 1.2 0.8

语法非常简单,通过以上代码就可以对相机的位置和缩放进行手动控制。同时,相机也有自动模式,使用 camera auto 进入自动相机模式后,相机会自动选取合适的位置和缩放比例,把场景中的所有角色肖像都包含进来。

另一个核心部分是调酒系统。虽然我在开发时一般会认为对话和调酒是两个部分,因为调酒系统涉及了非常非常多的其他内容。

但调酒系统应该是对话系统的一部分才对,因为游戏的核心玩法是根据玩家的调酒结果进入不同的对话分支,调酒系统在某种意义上只是一个更复杂的分支选项功能。

GDScript
order order_code

switch result
case correct >> jump correct_drink
case wrong >> jump failed_drink
case hidden >> jump hidden_drink

label correct_drink
Luca >> 你要的[last_drink_name],好了。
Alex >> 谢谢。
jump after_order

label failed_drink
Alex >> ……这不是我要的。
jump after_order

label hidden_drink
Luca >> 虽然这不是我要的,但是我喜欢这个!
jump after_order

在对话脚本中,使用 oder + oder_coed 即可触发调酒功能,然后使用 switch result 进入不同的对话分支。

oder_code 是这次调酒任务的代号,详细的调酒要求存储在一个 json 文件中。调酒系统会收到代号,并从 json 文件中找到详细的要求信息,然后在玩家点击“上酒”的一刻,计算出最终结果,例如“correct” “wrong”等等。这个计算过程还挺复杂的,甚至这一部分的代码长度已经超过了 DialogueParser,所以我觉得没必要展开。

风味和技法选项
原创饮品 – 这个 UI 还在调整中,不代表最终表现

为了支持“原创饮品”的功能,每一杯酒都会被从甜度、酸度、苦度等等多个方面进行评分。在计算结果时,除了可以按照固定配方进行匹配,还可以通过判断分数是否处于目标区间进行匹配。

调酒系统有许多内置变量,可以在对话系统中使用方括号语法插入,例如 last_drink_name 表示上一次调的酒的名字,last_drink_ingredients 是那杯酒的配料。

GDScript
Luca >> [last_drink_name],好了。
Alex >> 里面有:[last_drink_ingredients]?
Luca >> 技法是:[last_drink_technique]。

这些变量其实存储在 GM 单例中,正常的语法本该是 GM.last_drink_name,不过因为调酒香港信息较为常用,所以如果一个内联变量没有点语法,则默认它来自 GM 单例。

除了对话和调酒以外,还有很多细枝末节的小更新。我每天都有记录当天完成的内容,所以我还能大概记得我这个月都做了哪些可以归类为“其他”的工作。

除了那两个代码为主的部分,其他的更新都是和美术效果相关的。例如 VHS Shader、辉光按钮以及上文提到的视差背景效果,对提高游戏的质感都有挺大帮助。

例如这是在应用了 VHS 着色器并调色之后的效果。同时也优化了 UI 的显示。

这是查看历史对话的 UI,使用了类打字机的设计,可以将最后一行对话滚动到屏幕中央。

除了美术之外,这个月我还为游戏的翻译工作做了准备,并且在之后为游戏加入功能都时候,都要同时考虑到翻译的问题,这是为了避免翻译问题像滚雪球一样越滚越大。

别的就没有什么了。

六月的计划

以上就是五月的开发进展,进入六月,期末月,显然开发时间就没有那么多了。

我的打算是在六月先完善 Steam 商店页,尽早公开这个游戏,尽可能拿更多的愿望单,虽然完成这个游戏的时间可能还需要好几个月。

不过我本来给《糖水俱乐部》的定位就是全流程只有几个小时的小游戏,所以我也会控制这个游戏的体量,避免我尝试把我想到的所有点子都加入游戏。

商店页计划在端午节上线,敬请关注。