Spring Lisp Game Jam 2024 已落幕。其实我之前关注 Lisp Game Jam 这活动也有很久了,往届的 Lisp Game Jam 也不乏一些作品令人眼前一亮, 今年我也算难得第一次有时间参加这样的线上比赛活动,干脆就记录一下参赛的过程与感受好了。 简单介绍一下这活动的一些主要的规则:
- 必须使用一种 Lisp 方言来编写游戏。不过根据主办者的观点, S 表达式是必备因素,比如 Dylan 是不算的。
- 游戏的主体逻辑必须从零开发,期限为 10 天。
- 游戏本身及其使用的引擎/框架必须开源,游戏的素材必须是免费的并且带有使用许可。
然后就是为了这个 Jam 进行的一些准备了。在方案上,考虑到时间的有限,我保守地选择了自己熟悉且验证过的 claw-raylib (自动生成的 Common Lisp 的 Raylib 绑定), 毕竟我还是不太想碰到各种奇怪且自己不可控的问题,从而耽误了游戏整体的开发进度。这套方案的优点是性能很好并且十分稳定,毕竟 CFFI 已经非常成熟了, 加上 Raylib 对 OpenGL 的封装并不复杂,就算出了问题,落实到 OpenGL 的 API 上也非常容易解决。当然缺点是没法跑在浏览器上,这样对评分数量会有一些影响。 同时,在 Jam 开始之前,我还从自己其他的 CL 游戏项目中分离出了游戏框架 EON ,希望能够借此机会通过尝试不同风格游戏的开发来完善这个游戏框架的功能。 在游戏题材上,我选择了塔防类的 3D 策略游戏,原因无它,我单纯没开发过这类游戏,想要尝试一下。
接着就是游戏的开发过程了。在技术上没有遇到太大的障碍,一些游戏开发常用的组件,比如光照系统、粒子效果系统、 GUI 系统我在 EON 框架中已经实现好了, 底层的图像、音频等格式的支持由 Raylib 完成。游戏素材的查找花了不少的时间,好看一点又带有免费商用的许可可谓是少之又少。 在游戏的交互上,作为键盘党 Emacs 用户,我也基本是按照纯键盘操作来设计界面及其交互逻辑的,当然支持纯手柄操作也是理所当然的。 游戏逻辑上,敌人按照预定的路线向终点前进,敌人的血量、移动速度视敌人的种类与等级有所不同, 塔在敌人出现自己攻击范围内时进行了攻击,攻击的伤害、范围、逻辑视塔的种类与等级也有所差异。 游戏逻辑比较简单,没有使用逻辑与渲染分离以及行为树等设计,尽管如此,还是有不少细节上的问题需要考虑。 对于游戏关卡的设计,为了节省时间,我直接使用了为 2D 游戏设计的通用地图编辑器 Tiled 来完成地图的绘制以及敌人的编排,然后通过一些技巧将其渲染在 3D 场景中。
敌人是分波进攻的,并且可以有多条进攻路线,波与波之间有间隔时间供玩家进行决策和塔的建造,
敌人波在 Tiled 地图中的对象属性中通过 S 表达式来定义,这样直接就可以 read-from-string
进游戏:
在游戏的平衡性上,我也花了一些时间设计了敌人与塔的各项数值放在一个全局变量里,然后实时修改:
(defparameter *tower-types*
'((:square-1 :cost ((1 . 800) (2 . 1200) (3 . 1600))
:rate 0.75
:radius ((1 . 2.0) (2 . 3.0) (3 . 4.0))
:power ((1 . 15) (2 . 25) (3 . 35))
:model ((1 . "towerSquare_sampleF")
(2 . "towerSquare_sampleD")
(3 . "towerSquare_sampleE")))
(:square-2 :cost ((1 . 1600) (2 . 2000) (3 . 2400))
:rate 1.0
:radius ((1 . 2.0) (2 . 3.0) (3 . 4.0))
:power ((1 . 30) (2 . 50) (3 . 70))
:model ((1 . "towerSquare_sampleC")
(2 . "towerSquare_sampleA")
(3 . "towerSquare_sampleB")))
(:round-1 :cost ((1 . 1000) (2 . 1500) (3 . 2000))
:rate t
:radius ((1 . 2.0) (2 . 3.0) (3 . 4.0))
:power ((1 . 4) (2 . 8) (3 . 12))
:model ((1 . "towerRound_sampleA")
(2 . "towerRound_sampleC")
(3 . "towerRound_sampleE")))
(:round-2 :cost ((1 . 1500) (2 . 2000) (3 . 2500))
:rate t
:radius ((1 . 2.5) (2 . 3.5) (3 . 4.5))
:power ((1 . 6) (2 . 12) (3 . 16))
:model ((1 . "towerRound_sampleB")
(2 . "towerRound_sampleD")
(3 . "towerRound_sampleF")))))
(defparameter *enemy-types*
'((:dragon :animation (:idle "Dragon_Flying"
:death "Dragon_Death"
:attack "Dragon_Attack")
:base-hp 150.0 :speed 1.0 :base-bounty 800)
(:slime :animation (:idle "Slime_Walk"
:death "Slime_Death"
:attack "Slime_Attack")
:base-hp 50.0 :speed 0.5 :base-bounty 100)
(:bat :animation (:idle "Bat_Flying"
:death "Bat_Death"
:attack "Bat_Attack")
:base-hp 25.0 :speed 1.5 :base-bounty 100)
(:skeleton :animation (:idle "Skeleton_Running"
:death "Skeleton_Death"
:attack "Skeleton_Attack")
:base-hp 75.0 :speed 1.0 :base-bounty 300)))
游戏整体开发的耗时在一周左右,代码量约为 1500 行,主要在 SBCL 和 CCL 上测试,发布于 itch.io,源代码托管在 Github 上。
最终的总排名在 17/48 ,比预期稍微差一点,但也算是在意料之中,毕竟在创意上的不足拖了其他得分项的后腿。
Criteria | Rank | Score* | Raw Score |
---|---|---|---|
Entertainment - how enjoyable is it? | #11 | 3.334 | 4.250 |
Presentation - how does it look/feel? | #11 | 3.432 | 4.375 |
Overall | #17 | 3.040 | 3.875 |
Creativity - how original is the idea? | #31 | 2.353 | 3.000 |
不过我个人是将这个作品的开发当作是在有限的时间内,使用自己的游戏框架来开发一个完整的游戏的一次尝试, 并借此机会优化框架并提升游戏开发工作流,同时我也收到了很多玩家的反馈和建议, 所以总结下来是一次很有意义的参赛经历。
然后聊一聊其他的参赛作品,比较意外的是可在网页中游玩的游戏里,之前 Fennel + LÖVE 2D 方案的垄断态势被打破了, 新出现了不少基于 WebAssembly 的方案,例如 Guile Hoot + HTML5 Canvas 和 S7 Scheme + Raylib ,对于小游戏而言画面表现力都还不错。 同时,参赛作品的数量基本也在逐年增加,从 2022 年的 14 个作品,到 2023 年的 29 个作品,再到今年 2024 年的 48 个作品, 希望明年能见到更多有趣的方案和作品出现。