前几天,我参加了为期9天的 Bevy Game Jam 4,要求使用 Bevy 游戏引擎开发,最后完成提交了一个鸭子滑冰吃面包的解谜游戏。在这次 Game Jam 的过程中,我从零开始上手这个以 Rust 作为编程语言的数据驱动游戏引擎,单人开发负责了游戏所有的游戏设计、编程、美术,对我来说是一个非常有趣的体验,因此想记录一下,并且也能给希望尝试 Bevy 引擎作为开发工具的游戏开发者一些参考。
游戏链接:QUACK!!! on ICE
对于想了解 Bevy 的可以看一看官网的介绍,我在这里就不多赘述了:Bevy Engine
背景
在做完我的第一个正式上架的独立游戏《小石头人踏上旅程》之后,我在做游戏这件事上处于一个停滞的状态。一方面是感觉长时间没有足够有趣的新点子想出来,另一方面是对一直使用同一样工具进行游戏开发有点厌倦(说的就是你, Unity)。在经过一段时间的停滞期之后,我决定要做点什么东西,不论想法好坏先试试再说。那天正好通过某个游戏开发直播得知 Bevy Jam 4 要开始了,自己也一直想尝试一下看上去很特殊的开源游戏引擎以及想体验下用 Rust 语言写游戏代码的感受。既能强迫自己想新点子还能上手新工具,这不是双赢的事情,于是我就很愉快地决定参加 Bevy Jam 玩一下了。
开发流程
头脑风暴阶段
既然已经决定要参加 Game Jam 了,剩下的事就是说干就干了。这次 Bevy Jam 4 的主题是 “That's a LOT of entities”,我要的第一件事就是根据这个主题来想一个游戏玩法出来。“Entities”是一个比较模糊宽泛的概念,可以指代:玩家操纵角色,敌人,阻挡物(地形)等等,至于如何表现“多”,可以是某个东西静态数量上的多,也可以让某样东西的数量动态增加。这个时候突然想到可以做一个同时喂饱很多鸭子的游戏。
好,现在至少有个初步的想法,就是做一个“同时喂饱很多鸭子”的游戏,然后我应该会做一个解谜。加上冬天的现实生活背景和我曾经喂过鸭子面包的经历,想到可以把“冰面滑行”作为一个主要游戏机制。于是,我在 Notion 里记录下了对这个游戏的初步想法草稿,并且取了一个看上去挺乐的游戏标题:
由于很多东西只在脑子里过一遍很难想象玩法是否好玩,以及要如何对这个粗糙的想法填充具体细节(比如鸭子吃面包数量有限定吗,吃完面包还能继续移动吗等等),最好能做一个简单的可以实际上手的东西试一下想法。我第一反应是在用 Bevy 正式开始做游戏前先用 PuzzleScript 这个解谜游戏原型工具来尝试一下,毕竟后者实现一个简单的游戏机制几乎不用花什么时间。尝试过后,虽然我在 PuzzleScript 里做的东西没法实现点击选择功能还有一些逻辑上的问题,不过已经能看出游戏设计的一些雏形了,比如可以用鸭子卡位的技巧来进行解谜之类的。对这个想法有了一定信心以后觉得是时候开始使用 Bevy 做一点东西了。
上手 Bevy 游戏引擎
在官网上,对 Bevy 是这么介绍的:“A refreshingly simple data-driven game engine built in Rust. Free and Open Source Forever!”,翻译过来就是:“Bevy游戏引擎是一款令人耳目一新的简单的数据驱动游戏引擎,用Rust语言构建而成。永久免费且开源!”。幸运的是,我之前一段时间正好在出于兴趣地学习 Rust,虽然不够熟练,但至少不用花大量时间入门学习该游戏引擎使用的编程语言。接下来的关键就是理解 Bevy 里的 ECS(实体组件系统),这也是这整个游戏引擎所围绕的思想。官网上提供了一个入门指南“The Bevy Book”,我很快就看完了(不是我看得快,是真的写得很短)。
把官网的入门指南看完以后,我也只是对整个游戏引擎有最基础的认知,可是对做一个游戏出来还是无从入手。我觉得是时候试试例子驱动的学习方式了。除了官方提供了很多实现基础功能的例子以外,我找到了 Bevy Jam 3 的第一名的开源代码来参考。在实现某个功能没有思路的时候我就搜索例子看看别人是怎么实现的。
开发的每个阶段都有肉眼可见的里程碑会比较有开发动力,因此我做的第一件事就是在屏幕上显示一个鸭子并且能用方向键移动,并且以格子为移动最小单位。这时是 Game Jam 的第二天。
实现基本功能
接下来要做的事是能够进行关卡/场景的读取,这件事我本来想用如 LDTK 之类的插件来做,后来发现由于我用的 Bevy 版本太新了但是插件没有及时更新,所以我用不了那些插件了。于是想着干脆仿照 PuzzleScript 里面用字符表示关卡的方式,从 .txt 里读取关卡,编辑关卡也是编辑 .txt 文件。虽然这个方法看上去很原始人也不方便,但是就一个 Game Jam 来说应该可以一试,实在不行之后再改进呗。于是我写了一个从 .txt 里读取关卡再存到数组里去的功能,游戏中的关卡实际上在加载进去之前是被这样表示的,不同的字符表示不同的物体:
这下老师傅手打关卡了,不过重要的是能快速地实现这个功能,我对自己的做法还挺满意的,接下来就是游戏逻辑代码。由于关卡被读到数组里去了,所以逻辑也都可以在数组里计算,这算是一个比较方便的地方。有了之前那些使用经验以后,鼠标点击选中鸭子的功能也做得较为顺畅,现在终于有一个虽然没有什么关卡设计但是能玩的场景了,鼓掌。
完善游戏机制
之前写的有关游戏机制的想法毕竟只是一个草稿,现在有能够亲自上手摆弄的场景以后,终于可以把之前悬而未决的一些事情决定一下。冰面滑行只有碰到障碍物才会停下这个是一开始就决定好的,不过在玩法的其他方面还有一些模糊的地方。很显然如果一只鸭子能无限制吃面包的话,游戏会变得过于简单,多只鸭子的设定也会没有必要,所以一只鸭子应该最多只能吃一个面包。这样鸭子就被区分为“饱”和“不饱”两个状态,为了能让鸭子之间能互相合作解谜,所以吃完面包之后应该还是可移动的状态。另外每关的目标被定为所有面包被吃完。
这里就不按照时间顺序,顺便提一句在开发后期加入“破冰”机制的事。在 Game Jam 的第七天左右,走在回宿舍的路上突然想到可以做一个破冰的机制,第二天醒来想到如果只有吃饱的鸭子在碎裂冰面上才会破冰会格外有趣,不仅符合逻辑也在游戏玩法上更有深度(这样就对鸭子两种状态从特性上做出区分)。所以我就紧急加了这个破冰的机制,确实带给了我关卡设计方面的新点子,让游戏变得更加有趣了。
改善美术
我从来不是很擅长做游戏美术,不过倒是积累了不少偷懒取巧的方法。比如在《小石头人踏上旅程》里用 MagicaVoxel 做体素模型,还有用后处理效果啥的增加画面质感之类的。不过这次我主要是想到了可以在配色方面下功夫,想办法改善之前几分钟做完的临时的简陋的像素美术和游戏界面。从身边一些做过设计的人那里经常会发现在做界面设计的时候会先定下主题色,于是我如法炮制,先从鸭子身上选取了标志性的颜色,还有冰面和面包的颜色,然后按照感觉选了一个看上去舒服的主题色板:
选完颜色后,我按照选出来的主题颜色用 Aseprite 重新画了一些美术素材,不过因为严谨的像素美术画起来比较麻烦而我实际上也不是很会像素画,所以我对自己的要求就是类似 Baba Is You 里面的简单的画法就行。改进前后的效果还是很可观的,算是用最少的时间成本大大改善了游戏观感。这些工作是在 Game Jam 的第7天左右完成的。
改前:
改后:
夜间模式背景的效果(Dark Mode? Duck Mode!):
顺带一提,你可能会在之前的一些截图里发现我把鸭子叫的拟声词“quack”错误地写成“quark”(夸克)了,后面发现了就改过来了。
遇到的坑
Bevy 还是一个处于频繁更新的开发中的游戏引擎,因此得自己想办法解决遇到的各种奇怪问题,也有的问题是我不熟悉新的工具导致的,总之是踩了一些坑。首先是由于 Bevy 的 API 变化频繁,看别人代码里使用的方法自己这里不一定能用,需要多查文档和官方例子。然后我在导出到不同平台的时候也出现了一些问题,比如导出 WASM 版本的时候需要清理代码里用到 std 标准库的地方,比如不能使用 std::fs 里的方法来读取文件,而是要换成用 include_str! 这个宏。成功生成 WASM 游戏版本以后,发现在网页上还是不能正常运行,查了半天发现这原来是 Bevy 引擎在 0.12 版本新引入的问题,需要手动禁止使用 .meta 文件才能解决。以前我用 Unity 的时候几乎没有这样的体验(指翻找 Github 上的 issue)。
完成提交
在技术问题被解决得差不多之后,我开始设计关卡了,由于还没做关卡编辑器,所以就手动在文本文件里编辑关卡,不过好在字符串看上去还是相对直观,做关卡的过程还算顺畅,一共整了 13 关。不过遗憾的是因为没有关卡编辑器,所以做不了比较复杂的关卡,整体难度上比较简单,但我相信还是保留了一些趣味的。在最后一天前有群友建议我可以加一个 undo 撤回的功能,我之前觉得做这个太花时间就一直没做(毕竟我上一个游戏的撤回做了挺久),后来想了下其实由于关卡都存在数组里了,所以应该不难做撤回。所以在提交前我紧急实现了一个撤回的功能,事实证明这是一个正确的决定。
在提交前,我也让认识的人试玩了一下。有人反馈一开始的鼠标点击鸭子操作不够直观,他没反应过来要鼠标点击,于是我在第一关额外加了点击的提示。破冰操作那里有人反馈缺少直观的引入机制关卡,于是一个更加直观的引入关被加入到游戏中。除此之外,在提交前我还删掉和改了一点作用不大的关卡。
接下来就是提交和试玩了,Bevy 社区里的大家还是挺热心的,留了不少评论和夸奖,我也试玩了一些 Bevy Jam 里别的作品。
Bevy Jam 4 的全部作品:Bevy Jam #4
总结
这次参加 Bevy Jam 对我来说是一个挺愉快的体验,不仅做了一个自己觉得比较有趣的游戏,还上手了新工具。Bevy 这个游戏引擎的使用体验上和我以往用过的游戏引擎很不同。首先是目前它没有图形界面,所以一切基本都在代码里完成。写代码的时候感觉自己在做完整的游戏而不是在 Unity 里东一块西一块写东西和拖拖拽拽(乐)。至于 Bevy 特色的数据驱动的 ECS 实体组件系统用起来其实开发效率挺高,用 Rust 来写游戏开发代码也是颇为有趣的体验。不过功能的全面性和稳定性自然是不比目前成熟的游戏引擎,不少问题也得自己想办法解决和搜索。我当时和别人说了一个有点夸张的比喻,用成熟商业游戏引擎做游戏就像用打火机点火,虽然方便但是没成就感,用 Bevy 就像钻木取火。
不过要问我之后是否会继续用 Bevy 做游戏,我的回答大概率是 yes。虽然如果做中长期作品可能还是会偏向其他成熟的游戏引擎,不过至少这次用 Bevy 打 Game Jam 的体验还是不错的,期待 Bevy 之后的更新和进步吧。另外我在考虑整理下代码,给这个游戏搓一个自己用的关卡编辑器啥的,这都是后话了。
以及欢迎试玩鸭子游戏 :)
赞!画得也很可爱!