新浪科技

如何用2 KB代码实现3D赛车游戏?2kPlus Jam大赛了解一下

机器之心Pro

关注

作者:Frank

机器之心编译

参与:王子嘉、Geek AI

控制复杂度一直是软件开发的核心问题之一,一代代的计算机从业者纷纷贡献着自己的智慧,试图降低程序的计算复杂度。然而,将一款 3D 赛车游戏的代码压缩到 2KB 以内,听起来是不是太夸张了?本文作者 Frank 是一名资深游戏开发者,在本文中,他详细介绍了如何灵活运用代码压缩、编译、随机数生成、代码复用、设计模式等十八般武艺仅仅通过 2KB 的代码就能实现一款强大的 3D 赛车游戏。

几个月前,当我听说传奇 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」就是一些很好的例子,但还有很多其它的小问题。因此,在阅读这段代码时,请记住,你经常会看到一些重复代码,这些重复是有目的的——便于压缩。

CodePen

下面我们将给出一款在 Codepen 上运行的游戏。你可以在 iframe 上玩这个游戏,但是为了获得最佳的游戏体验,我建议你使用链接(https://codepen.io/KilledByAPixel/pen/poJdLwB)打开它,这样你还可以编辑或是创建代码分支。

HTML

我的游戏使用到 html 的部分很少,因为它主要是基于 JavaScript 开发的。JavaScript 创建全屏画布的方法和与后面将画布大小设置为窗口内部大小的代码都是最节省空间的。我不能确定为什么 CodePen 中需要将「overflow:hidden」添加到「body」标签中,但是直接打开应该也可以正常工作。

最终的压缩版本使用了更小的设置——把 JavaScript 包在一个「onload」事件的「call」方法里()。但是,我不喜欢在开发的时候用这种压缩的设置,因为代码是以字符串形式存储的,这样编译器也就无法正常地强调语法。

极致压缩

整个游戏的业务逻辑就是如此!以下是用彩色编码将其压缩以显示不同部分后的最终结果。完成所有这些工作之后,可以想象,在这样一小段代码中看到我的整个游戏是多么令人满足。之后的 zip 操作通过消除重复的代码将文件大小几乎又减少了一半。

HTML – Red

函数 – Orange

设置– Yellow

玩家更新 – Green

背景渲染 – Cyan

道路渲染 – Purple

对象渲染 – Pink

HUD 渲染 – Brown

说明

其他方法也可以实现同时提供性能和视觉效果的 3D 渲染。如果我有更多的空间,我更愿意使用像「three.js」这样的 WebGL API,我在我去年制作的游戏《Bogus Roads》中就用到了它。此外,因为它使用的是「requestAnimationFrame」,所以确实需要一些额外的代码来确保帧速率限制在 60 fps,我将它们添加到了增强版本中。我更喜欢使用「requestAnimationFrame」而不是「setInterval」,因为它的渲染结果更平滑,因为它将被垂直同步(让显卡的运算和显示器刷新率一致以稳定输出的画面质量)。这段代码的一个主要有点是它的兼容性非常好,可以在任何设备上运行,不过在我那台老旧的 iPhone 上运行速度有点慢。

结语

加载中...