Spring Lisp Game Jam 2024 赛后感

Spring Lisp Game Jam 2024 已落幕。其实我之前关注 Lisp Game Jam 这活动也有很久了,往届的 Lisp Game Jam 也不乏一些作品令人眼前一亮, 今年我也算难得第一次有时间参加这样的线上比赛活动,干脆就记录一下参赛的过程与感受好了。 简单介绍一下这活动的一些主要的规则:

  • 必须使用一种 Lisp 方言来编写游戏。不过根据主办者的观点, S 表达式是必备因素,比如 Dylan 是不算的。
  • 游戏的主体逻辑必须从零开发,期限为 10 天。
  • 游戏本身及其使用的引擎/框架必须开源,游戏的素材必须是免费的并且带有使用许可。

然后就是为了这个 Jam 进行的一些准备了。在方案上,考虑到时间的有限,我保守地选择了自己熟悉且验证过的 claw-raylib (自动生成的 Common LispRaylib 绑定), 毕竟我还是不太想碰到各种奇怪且自己不可控的问题,从而耽误了游戏整体的开发进度。这套方案的优点是性能很好并且十分稳定,毕竟 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 上。

demo

最终的总排名在 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 个作品, 希望明年能见到更多有趣的方案和作品出现。

19 个赞

素材问题确实很严重, 感觉现在各种AI生图、生视频、生音乐工具那么火的一个原因就是人们会认为素材是AI做的, 就可以随意用规避版权问题

2 个赞

题主用 Lisp 开发游戏的主要原因是什么?在我的理解中,游戏速度如果慢的话,这款游戏基本就失败了。

坛子里真的是藏龙卧虎呀

Guile Hoot 的项目我大概了解一些。

首先 Spritely Instituite 的项目 Goblins 据说是要「重建网络」,这个理想有点类似于 GNU 之前的 GNUnet 项目。然后他们要用 WebAssembly 和 Scheme,然后拉上了 Guile 的作者 Andy Wingo 一起做(正好 Andy Wingo 对 Guile 以前的一些历史遗留设计也不甚满意,双方一拍即合)

这个游戏,我觉得更像是一个他们的概念验证,证明 Hoot 和 Goblins 的 WebAssembly 开发能力。就像他们文章里写的,用到了 Goblin 的 sprite 系统和事件循环(Goblin 本身也是一种类似于 Actor 模型的框架)。

游戏本身还有点意思,而且加上 WebAssembly 技术本身的噱头,最后这个好像是排了第二名

1 个赞

你可以考虑看看这个RacketCon 2013_ Dan Liebgold - Racket on the Playstation 3_哔哩哔哩_bilibili, 顽皮狗是如何用Racket开发最后生还者的, 其实之前顽皮狗就神秘海域3也做过类似的演示. 顽皮狗在这之前也使用Lisp开发过古惑狼以及杰克与达斯特.

我追求纯代码的游戏开发,这样方便使用像 Git 这样的版本控制工具来管理基本所有的游戏内容, 并且能够自底向上地对游戏的一切进行完全地掌控,而不需要遵循游戏引擎的条条框框, 也不需要使用游戏引擎指定的脚本语言。不过游戏开发中使用脚本语言的需求是真实存在的, 但我始终认为使用相同的语言来完成游戏框架、底层逻辑、脚本的开发是最优雅的方案, 而不是目前主流的底层 C++ 然后上层 C# 、 GDScript 、 Lua 的方案。 这就要求一种语言必须同时具备不错的性能、灵活度、动态性, 这么一看选择其实并不多,再排除一些像 Julia 这样重度依赖 JIT 的语言, 那基本只剩下 Common Lisp 和 Scheme 了。两者中从语法与功能的稳定性、交互性, 以及对可变状态的友好程度上, CL 都占优,所以 CL 是非常适合纯代码的游戏开发, 这点从 CL 在 Lisp 方言中丰富的游戏开发生态也可以看出。仅从性能的角度来看,首先我认为速度只能算游戏的一小部分, 对于非动作和非竞技类的游戏,逻辑更新速率有 60FPS 就够了,玩法和游戏性才是游戏的内涵。 其次是矩阵、向量等图形相关的计算,不需要在 CPU 上完成,就算是 Python 配合 GLSL 也能随便做到 240FPS 的渲染帧率 。 最后, CL 的计算性能也不差, SBCL 稍微优化一下基本能达到 C++ 一半的水平,不考虑可移植性地用上一些 SBCL 的扩展, 性能就更好了。

3 个赞

我以前在一个音频项目里试过一下,结论是SBCL比较使劲优化以后能达到没优化的C++一半的性能。当然这个性能确实也够很多类型的游戏达到60FPS了。

https://loglog.games/blog/leaving-rust-gamedev

之前有个很火的批判 rust 游戏开发的博客可以看一下,大概就是游戏工程需要大量的快速迭代(这个人也用过 Common Lisp 来试验开发)

1 个赞