每个人都有那样一个文件夹。里面塞满了半途而废的教程、被遗弃的侧边项目,以及那些承诺远超其实际内容的 README 文件。那是良好意图的坟场,而唯一的出路就是发布一些——任何东西——真正能运行的东西。
那种想要完成的渴望促成了 LIAR 的诞生。这是一款如此之小、如此“刻薄”的浏览器游戏,它仅包含在一个 28KB 的单个 HTML 文件中。没有框架。没有图像文件。没有音频资源。只有原始的 HTML5 Canvas、Web Audio API,以及一点点“坏心眼”。
反教程
大多数学习项目之所以夭折,是因为它们变得过于臃肿。你从一个 Canvas 教程开始,添加一个移动脚本,然后决定你需要一个瓦片编辑器,接着是一个实体组件系统(ECS),然后是一个编译需要 11 秒的 Webpack 配置。六周后,你拥有了一套构建流水线,却依然没有游戏。
LIAR 拒绝掉入那个陷阱。它是一款反应游戏。一个按钮会告诉你:点击(TAP)、按住(HOLD)或不要动(DON'T)。你必须在计时器结束前做出反应。前几轮用于建立信任。文字显示 TAP,你就点击;文字显示 HOLD,你就按住手指。它简单、有节奏,甚至带有一种冥想感。
然后,第六轮来了。
背叛的机制
大约在第六轮左右,游戏开始撒谎了。屏幕上会闪过一个指令——也许是一个剧烈、红色、颤动的 TAP——你之前在热身阶段建立的所有反射神经都会尖叫着让你按下按钮。如果你照做了,你就输了。那红色的颤动并不是故障,而是一个“暗示”(tell)。一个诚实的骗子会给你线索,而这款游戏恰恰提供了这一点:一套视觉上的欺骗语法。
这个技巧之所以奏效,是因为它的基础是诚实的。前几轮教会了你颜色和动作带有含义。当色调突然改变且文字开始颤动时,游戏并不是在作弊,而是在发出信号。你已经掌握了信息,要么读懂了它,要么没读懂。
当你失败时,游戏会羞辱你。这种刺痛感是刻意设计的。在一个没有排行榜、没有持久进度积累的微型游戏中,羞辱感是提高重玩率的货币。它闭合了反馈环。你输掉并不是因为延迟,也不是因为判定框(hitbox)有问题。你输是因为你相信了一个颤动的红色单词,而游戏确保你会记住这个错误。
28KB 究竟意味着什么
选择构建在一个 28KB 的单个 HTML 文件中不仅仅是为了新奇。这是一种迫使你做出艰难决策的设计约束。你没有数兆字节的精灵图(sprite sheets)可以躲避,也没有音频文件可以触发。所有可见的内容都是在 HTML5 Canvas 上逐帧绘制的。所有可听到的声音都来自 Web Audio 振荡器(oscillators)。
如果你没有直接使用过 Web Audio API,以下是它的工作原理:代码不再加载预录制的 WAV 或 MP3,而是创建一个 AudioContext,附加一个振荡器节点(oscillator node),选择一种波形——正弦波(sine)、方波(square)、锯齿波(sawtooth)——并设置频率。输入正确时发出短促的哔声,失败时发出刺耳的嗡嗡声。这些音调由浏览器实时计算。它们几乎不占用文件大小,也不会产生额外的网络请求。
同样的逻辑也适用于视觉效果。Canvas 为你提供了一个立即模式(immediate-mode)的绘制表面。你清除矩形,填充文本,为颤动效果设置变换(transform)或阴影模糊(shadow blur)。这里没有虚拟 DOM 的差异比较(diffing),没有 React 的协调(reconciliation),也没有需要调试的依赖数组。浏览器运行代码,像素随之出现。对于这样一款专注的游戏来说,这种直接性是一种特性,而非限制。
将负载保持在 28KB 还意味着游戏在移动浏览器上几乎可以瞬间加载,即使在网络不稳定的情况下也是如此。它的托管成本几乎为零。整个应用程序比大多数网站的 favicon 还要小。如果你通过共享链接分发或嵌入论坛,这一点非常重要。它没有任何安装门槛。你只需访问 URL,谎言便已准备就绪。
公平的欺骗
在“撒谎机制”上投入大量时间揭示了游戏设计中一个微妙的真理:不公平只有在“可读”时才有趣。一个在毫无预警的情况下杀死玩家的随机陷阱是糟糕的设计。而一个通过震动、改变颜色并打破既定视觉节奏的陷阱,则是一个伪装成伏击的谜题。
红色颤动的文字具有多重作用。它覆盖了玩家在第一到第五轮中建立的习惯性反应。它在原本的反射测试中引入了情感波动——怀疑、犹豫、恐慌。最重要的是,它让失败感变得“理所应当”。当你点击那个红色指令并看到羞辱语时,你知道错在自己。游戏给了你暗示,而你没能读懂它。
这就是“卑鄙偷袭”与“公平对决”的区别。LIAR 想要挥拳击向你的脸庞,但它在挥拳之前,会先蓄足全身的力量。
先交付,后打磨
这款游戏还很粗糙。作者本人也会这么说。这是他们完成的第一款游戏,而“简单”是换取“完工”的代价。在开发者们经常撰写关于那些从未投入生产的系统的博客的生态系统中,这种诚实显得弥足珍贵。LIAR 拥有一个在线 URL,你现在就可以玩。你可以在单个文件中查看源代码,无需去理清复杂的 monorepo。
开发者想从任何有胆量尝试的人那里了解两件事:第一,你能生存多久?第二,当第六轮出现第一个谎言时,你会觉得这很公平,还是觉得这很卑鄙?这些才是真正该问的问题。他们并没有将“谎言”视为 bug,而是将其视为一种需要调优的游戏机制。
如果大家感兴趣,作者已提议撰写一份更深入的指南,涵盖 Canvas 绘制循环、Web Audio 振荡器背后的逻辑,以及谎言机制在代码中究竟是如何构建的。考虑到在一个 HTML 文件中看到完整游戏是多么罕见,这份指南对于任何困在“教程炼狱”(tutorial purgatory)中的开发者来说,都将极具参考价值。
真正的启示
你不需要游戏引擎来构建游戏。你不需要用于资源分发的 CDN,不需要长达二十页的设计文档,也不需要框架维护者的许可。你需要的是一个边界——在这里,是 28KB 和单个文件——以及一个完成它的理由。约束会迫使逻辑变得清晰。当底层的机制足够扎实时,一个颤动的红色单词,远比一千个导入的精灵(sprites)更令人印象深刻。
亲自体验 LIAR:https://playliar.netlify.app/。
你可以在这里阅读开发者原始的拆解分析和源码背景:https://dev.to/shabbir_sesaifee_8fdc587/i-built-a-game-that-lies-to-you-in-a-single-28kb-html-file-no-framework-no-assets-4hnh。
