Github Gameoff 2025 参赛记(a.k.a 独立游戏 Waveforge 开发记)
很早就关注到 Github Gameoff 这个系列的 Game Jam 比赛,但是一直没有参加过,总是感觉做游戏这么高级的东西,肯定离自己很遥远吧。今年又在刷 Github Explore 时看到了,点进去,比赛的介绍页面上还写着新手可以考虑使用的游戏引擎等等建议,仿佛这个比赛完全是“For Beginners”的,忽然觉得游戏开发好像也就那么回事,立即就报了名。
我之前也做过一些小游戏,不过大多是用 HTML5 的 Canvas API 弄的,大多画面粗造。何况用这个技术做游戏,参加 JS13K 可能很合适,但是一想到大部分人都会用各种专业工具引擎,就感觉实在是相形见绌。
“我需要一些帮手。”
于是,那一天,我遇到熟人就问:“你有什么才能?”,在 SUSTech,会编程的人十有八九,可是制作游戏所需的艺术人才却寥寥无几。最后,只有 stevvven 答应试着做做音乐相关的内容。
11 月 2 日星期日,我早上 10 点就早早起床了,一起床就打开邮件,就看到了“Gameoff 2025 主题已公布!——Waves!”的邮件。
主题是 WAVES!这大大超出了我的预期。去年的“SECRETS”,发挥空间很大,我还以为 Game Jam 的主题都是这种大词呢。不过,查了字典才知道,其实 WAVES 的含义也有很多,做名词时,可以是各种波:
- 水波、海浪
- 声波
- 电磁波(光?)
- 怪物波次
- 甚至是引力波、脑电波之类的
做动词还有一种截然不同的意思:
- 挥手、打招呼
我和舍友头脑风暴了一下。当时思考了很多方向,其中一个现在仍觉得很有意思的是这样一个塔防游戏:玩家的防御塔不能直接攻击,而是发出声波“劝说”怪物放弃进攻,地图里的障碍物会阻挡或扭曲声波,不同的声波还能叠加或抵消。
最后,鉴于这是我第一次参加 Game Jam,我就想做得简单一点(当然,最后事实证明,这一点也不简单),做一个 2D 平台跳跃游戏,这类游戏非常经典,有很多成功经营可供参考。游戏里的玩家是个微缩小人,在一个牛奶玻璃瓶里跑酷,瓶子里有些水,需要善用这些水来完成跳跃,说不定瓶子还会倾斜翻倒之类的,改变重力方向。
再后来,我想,为什么不反套路一点呢?玩家不能直接控制角色,而是要通过控制水和波浪,把角色冲来冲去达到目标。想到这儿,大家都觉得这个点子不错,于是核心玩法就这么定了。名字也就顺理成章地定为 Waveforge。
确定了点子,我就立即着手去研究技术选型。我不太想用 Unity 或 UE 这种比较重量级的引擎,一方面是不太喜欢 C# 这种 Windows 专用的语言,和我的 Linux 开发环境也不兼容。考虑到Github Gameoff 是比较长时间的 Game Jam(从 11 月 2 日开始,一直到 12 月 1 日截止提交,有整整 4 个星期还多一点的时间),我就在网上寻找小众一点的引擎,偶然刷到一个学生大作业设计的引擎 Delta Forge,里面实现了“falling sand”风格的物理系统。这让我一下子想起了这类物理模拟的集大成之作—— Noita(2020)的“Falling Everything”引擎。感觉这种基于胞元自动机物理系统会非常有趣,于是我决定,我们也要用这种基于像素的物理模拟引擎。自然而然地,游戏风格就确定了:像素画风 + 侧重物理解谜的玩法。我当时立即就开始研究如何自己实现一个“falling sand”物理系统了(Falling Everything 引擎是闭源的)。很多具体的玩法其实是之后才细化的,最开始只有一个模糊但感觉很棒的概念。最终,这个概念得到了不错的贯彻执行。
定下了核心物理机制,我没多想,直接在YouTube上找了两个视频学习:一个是Noita 开发者的技术访谈,另一个是其他人模仿 Falling Everything 的心得报告。这两个视频很有启发性,但缺少技术细节。说干就干,我制定了一个简单的全过程计划:
- 第一周:物理系统技术验证,游戏概念验证
- 第二周:完成游戏核心逻辑设计
- 第三周:完善UI并进行关卡设计
- 第四周:获取早期测试结果,修改漏洞,进行宣传
11 月 2 号当天,我就新建了一个 C++ 项目开始动手实现,遇到困难就回去反复看视频。没过几天,大部分问题都解决了,但我遇到了一个巨大的挑战:这些物理模拟中的水,并没有真正模拟水压,而是形而上学地模拟了水的流动行为。这导致在U形管的一边注水,另一边液面不会上升,非常不符合现实。我专门打开《Noita》,装了个沙盒模组测试,发现强如 Noita 也没有解决这个问题。对 Noita 这种核心玩法是法术组合的游戏,水不真实无伤大雅,但对我这个以水为核心玩法的游戏来说,这是无疑是致命缺陷。
我花了数天研究这个问题,考虑了SPH(光滑粒子流体动力学)和 Stable Fluids 等多种方案。IOCCC 2012有个获奖作品就是用 SPH 实现的简单液体模拟。可惜,我无法直接迁移这个算法:SPH天生不适合处理像素化(网格化)的地图,很难应用边界条件,水的特性难以为了游戏性微调,计算量大,甚至无法保证水量严格守恒。
我也考虑了基于 Navier-Stokes 方程的 Stable Fluids 算法,它物理基础坚实,适合网格分析,易于应用边界条件,且能保证水量守恒。但同样有问题:计算量很大,算法复杂(光实现可能就要一两个月),同样难以微调。
最后,我决定还是回到 Falling sand 那种形而上的思路,不真正处理水压,只模拟表象。但传统的胞元自动机思路似乎走到了穷途末路,我决定引入对整个地图的全局分析。我自己设计了一个启发式算法:利用洪水填充算法找出水的连通块,对于每个连通块,找到其最高的液面和较低一些的液面,然后将最高处的液体直接转移到较低的液面上方。这个算法在水上表现很好,计算量小(精细实现是 \(O(n)\)),而且有很多参数可以为了游戏性调节。
我对上面的方案非常满意。然而,在我引入“油”(不溶于水且密度比水小的液体)之后,问题出现了。在U形管中先加水再在一侧加油,油本应把水压下去,使另一侧水面上升。但我的算法会把高处的油直接“穿越”到另一侧的水面上。当液体都是水时,玩家看不出来,一旦有油,就原形毕露了。
我本可以继续设计各种启发式的“歪招”来解决,但我想要一个通用算法,即使在一个九曲十八弯的管子里倒入鸡尾酒,也不会发生任何(可见的)穿越现象。我想了很久都没头绪,以至于打算上床玩手机。结果刚躺下没多久,一个完美的算法就冒了出来(这充分印证了“遇到困难睡大觉”的正确性):我可以把这个问题抽象成图论中的网络流模型!将每个连通块中的同种液体抽象成一个点,两个液体有相邻像素就连边,边的流量定为相邻像素对的数量(即一个 tick 内可交换的像素数),然后包含最高液面的点连一条边到抽象源点,包含较低液面的点连到抽象汇点。我用Dinic算法在这个图上求最大流,根据边的流量在液体间移动像素即可。可笑的是,这算法叫网络“流”,却天天被用于组合优化问题,而我真正需要分析物理水流时,竟然花了这么久才想到。
在我专心与胞元自动机斗智斗勇时,一个隐患悄然埋下——早期实验物理模拟时,我懒得自己写渲染系统查看效果,就让 Copilot 帮我写了相关代码,图形方面,AI 使用了 SFML 。我没太在意,毕竟本来只打算用它验证物理概念。但后来我不满意,又在上面增量开发,导致代码库和SFML耦合紧密(比如为了省事,物理引擎里直接用了 sf::Vector2f),最终已无法放弃 SFML。然而 SFML 只是个基础库,不是完整引擎,我只好在它上面自己开发了资源加载管理、场景管理器、简单数据驱动的UI、打包工具等等,硬生生把 SFML 拓展成了一个简易的自研引擎,反而费了更多功夫。这和刚才的水流挑战不同,没有高深算法,但非常繁琐,给工期管理带来了挑战。不过我也不后悔,毕竟积累了不少经验。
在第一周,stevvven 也非常给力:确定了像素艺术风格和“WAVES”主题后,他在第一周就提交了4首背景音乐(最后用了2首,主菜单和选关一首,闯关时一首。另外两首虽然很棒但有点吵闹,怕分散玩家注意力,忍痛弃用了)。后续他还提供了所有音效设计(他描述效果,我来编程实现),这些音效也很好地指导了动画制作。而且在这些事情之外,他甚至还在学校一个乐队担任鼓手,进行了一次公开演出。真是精力充沛。
除了投入物理系统的研发之中,第一周我还研究了不少成功的游戏设计。下面的内容节选自我的计划单:
- 研究 Noita(2020)、C::Reactris(2023)、超级鸡马(2016)的物理系统原理
- 研究 CATO 黄油猫(2024)、Öoo(2025)、ANIMAL WELL(2024)的关卡设计
- 研究 Undertale(2015)、Minecraft(2011)、Pokemon Game Boy Color(1998)的音乐风格
- 研究 Minecraft Bare Bones 材质包(2023)、Noita(2020)、CATO 黄油猫(2024)的美术风格
美术方面,我略懂一点像素画,前两周都是我 随便乱画 亲自操刀。第二周结束时,物理系统已经基本完成,我和几个朋友分享进度,其中 ZTL-UwU 非常感兴趣,希望参与美术设计。他重新设计了整个艺术风格,把“毛坯房”升级成了接近发行级别的“精装修”。在我埋头处理核心物理算法时,他还帮忙做了一些UI相关的编程工作。中途,重新设计艺术风格比预期花的时间长,幸好有惊无险地完成了。下次再有类似活动,我一定一开始就拉他入伙,而不是等到第三周才开始赶工、连续熬夜。
从第三周结束时,我们的游戏的核心机制趋于完善。但是由于美术设计的用时大大超出预期,还只有 3 个 Demo 关卡。在这一方面,我必须要感谢 Zurry 在玩法和关卡设计上提供了很多好点子(最后至少有 1/3 的关卡直接来自他,另有 1/3 深受其启发)。在他的帮助下,我们很快就完成了 12 个关卡(最后成品有 18 个关卡)。
另外,我和 ZTL 系统都是 Linux。为了构建用于 Windows 的发行版,我使用了 Github Actions 提供的 Windows 虚拟环境:在每次提交后都运行一个持续集成配置,在 Ubuntu 和 Windows 上分别构建程序,并上传结果到 Artifacts。这样,虽然很方便地有了构建物,我们还是没有办法测试构建出来的程序是否能正常运行(事实上,一开始是不行的),在这一点上,我必须感谢 RitaRossweisse301,是他不厌其烦地反复测试我们的游戏(即使很多时候完全不能运行或者轻易崩溃),并不断给出反馈建议。
得益于恰当的项目管理,我们留出了最后 5-6 天进行密集测试和反馈收集。我们修复了许多漏洞,并意识到游戏整体难度偏高,通关时间超出了我的预期(后期我每次大改后,为测试都会清空存档重新通关,这只需要10分钟,但测试玩家普遍用了1小时左右)。但我并没有因此降低难度——因为大部分玩家虽然感到困难,但都通过自己的独立思考完整通关了。参与测试的玩家可能本来就喜欢解谜游戏。我不希望因为降低难度而两头不讨好(休闲玩家依然觉得难,硬核玩家觉得太简单)。毕竟,我们面向的本来就是喜欢解谜游戏的玩家群体。
留出一周的决定是极为正确的。很多 Game Jam 中开发游戏从来不乏有趣的点子,但可惜的是,很多游戏都没有真正将这些想法落到实处:在 Game Jam 中有一个知名的"最后10%陷阱",即最后10%的工作往往占30%的时间。然而,我们还是有一周的时间打磨,最终呈现的效果就非常好。事实上,一个早期测试玩家评论:very cool idea, and well executed.
我们的游戏发布在 itch.io 上:https://fang-erj.itch.io/waveforge,欢迎试玩!游戏代码完全开源代码在Github:https://github.com/szdytom/waveforge




浙公网安备 33010602011771号