如何用2 KB代码实现3D赛车游戏?2kPlus Jam大赛了解一下
几个月前,当我听说传奇 JS1K 游戏编程竞赛将不再举办时,当即把这件事告诉了其他开发者,最后我们决定在 itch 上搞一个 2KB 版的编程竞赛以弥补这一遗憾,我们将其称之为「2kPlus Jam」。这个竞赛的主要目标是制作一个只需要 2KB 压缩文件就可以容纳的游戏。如果你知道一个 3.5 英寸软盘可以存超过 700 个这样的游戏,你也就知道这有多小了。
我的作品(Hue Jumper)是对 80 年代赛车游戏渲染技术的致敬。这里的 3D 图像和物理引擎是我纯粹地使用 JavaScript 从零开始实现的,同时我还花了大量时间去调整游戏玩法和视觉效果。
游戏编程竞赛强调「变化」(shift),所以每当玩家通过关卡时,我就会改变整个世界的色调。我想在玩家通过关卡时,会感觉到像进入了一个色调不同的新的维度,这就是我为它取名为「Hue Jumper」的原因。
本文包含了这个游戏的完整 JavaScript 代码,所以可能会有点长。不过代码的注释很友好,所以我不打算一行一行解读,也不要求你现在就通读所有代码。我的目的是向你解释它的工作原理,还有为什么我要这样做,以及这个项目的整个结构。你也可以在 CodePen上找到这份代码(https://codepen.io/KilledByAPixel/pen/poJdLwB),并进行现场调试。
那么,系好安全带,坐稳,我们要开始啦!
我的灵感主要来源于 80 年代的经典赛车游戏,比如《Out Run》。使用相似的技术,他们能够在非常早期的硬件上实现实时三维图形。我最近也在玩一些现代的赛车游戏,比如《Distance》和《Lonely Mountains: Downhill》,这些游戏也对我的视觉设计和游戏体验有所帮助。
Jake Gordon 用 JavaScript 写的一个伪 3D 赛车的项目(https://github.com/jakesgordon/javascript-racer/)帮我减轻了很多负担。他还为此写了一系列解释其工作原理的博文。尽管我的项目是从零开始的,但是他的代码助我解决了遇到的包括数学在内的一些问题。
我还看了 Chris Glover 开发的一款名为「Moto1kross」的 JS1k 游戏(https://js1k.com/2019-x/demo/4006)。这款简单的 1KB 赛车游戏给了我一个参考,让我知道什么是可能实现的。现在我有额外的 1KB 可用空间,因此我得远远超过它。
总体策略
由于游戏大小有严格的限制,程序的架构就显得尤为重要。我的总体策略是让一切尽可能的简单,以实现创造一款视觉感受和游戏体验都很棒的游戏的最终目标。
为了压缩代码,我用 Google Closure Compiler (https://closure-compiler.appspot.com/home) 来运行它。这个编译器会删掉所有空白,把变量重命名为 1 个字母的字符,并且做了一些简单的优化。你可以通过下面的链接使用这个编译器:https://clocompiler.appspot.com/home。
不过,这个编译器还做了一些无用的事,比如替换模板字符串、缺省参数和其它有助于节省空间的 ES6 特性。所以我需要手动撤销某些无用的工作,并预先准备一些更「冒险」的压缩技术,以节省每一个字节。但这并不是最主要的成功之处,大部分文件体积的压缩还是归功于代码本身的架构。
代码需要被压缩到 2KB 以内。如果你不想选用上一种方案,还有一个类似的、但功能较弱的工具——RegPack,它可以在严格遵守规定的情况下编译 JavaScript。无论哪种方式,策略都是一样的,尽可能使用重复的代码,然后在压缩的时候压缩它们。例如,某些字符串经常出现,因此它们的压缩比很大。「c.width」、「c.height」和「Math」就是一些很好的例子,但还有很多其它的小问题。因此,在阅读这段代码时,请记住,你经常会看到一些重复代码,这些重复是有目的的——便于压缩。
我的游戏使用到 html 的部分很少,因为它主要是基于 JavaScript 开发的。JavaScript 创建全屏画布的方法和与后面将画布大小设置为窗口内部大小的代码都是最节省空间的。我不能确定为什么 CodePen 中需要将「overflow:hidden」添加到「body」标签中,但是直接打开应该也可以正常工作。
最终的压缩版本使用了更小的设置——把 JavaScript 包在一个「onload」事件的「call」方法里(<body style=margin:0 onload=」code_goes_here」><canvas id=c>)。但是,我不喜欢在开发的时候用这种压缩的设置,因为代码是以字符串形式存储的,这样编译器也就无法正常地强调语法。
最新回答