「拧螺丝啦」:WorkBuddy 把一个微信小游戏"骗"进了浏览器

从 GitHub 拉了个叫「拧螺丝啦」的微信小游戏:零引擎、Canvas2D 自绘 3D、30 关。但它只能在微信开发者工具里跑。WorkBuddy 没改一行游戏代码,写了个 10KB 的适配层把浏览器伪装成微信,30 关无头全通。这回是真·拧螺丝。
一、名字就赢了一半
打工人自嘲,说自己天天在"拧螺丝"。所以当我在 GitHub 上刷到一个叫「拧螺丝啦」(nls_minigame,作者 MrZhaoZQ)的游戏时,就没忍住——这名字,打工人看了会心一笑。
拉下来一看,正经得吓人:
- 玩法:3D 空间规划 + 休闲解压 + 三消。旋转观察一堆叠起来的木板,把螺丝拧出来,同色三个一碰就消,木板坍塌,过关。
- 技术:微信小游戏,零引擎、零构建管线——纯 JS 加 Canvas2D 自绘 3D(自己写了 Vec3、矩阵、射线求交、画家算法渲染)。
- 质量:30 个关卡由生成器加校验器管线产出,近 200 项 Node 单测,包体只有 830KB(预算 4MB 的一小半)。
【插图 01 · 01-game.png】
一个休闲小游戏,工程做到这个份上,值得认真对待。
二、问题:它只肯在微信里跑
然后我就撞上了微信小游戏的经典门槛:必须装微信开发者工具,导入项目、编译、预览——一套流程走下来,玩的心情先凉一半。
我只是想现在、立刻、马上玩一下。
看了一眼代码结构,发现希望很大:游戏通过一个 Platform.js 访问平台能力,而且只认全局的 wx 对象——检测到 wx 就走微信分支。也就是说,只要有个东西能假装自己是微信,游戏根本不会发现。
三、WorkBuddy 的解法:不改一行代码,把浏览器伪装成微信
我把这事丢给了 WorkBuddy。它的方案很克制:游戏源码一行没改,在旁边加了一个 web/ 适配层,核心是一个 10KB 的 wx-shim.js——用标准 Web API 把 wx.* 的接口一个个实现出来,让游戏以为自己跑在微信里。
几个关键翻译:
createCanvas→ 在页面里建 canvas,接上指针和滚轮事件;- 触摸事件 → Pointer Events 转成微信的结构;
getStorageSync→ localStorage(加个nls:前缀);- 音效 → WebAudio 解码 wav,失败还有 HTMLAudio 兜底;
vibrateShort→ 浏览器的震动接口;- 广告、分享这些浏览器给不了的 → 返回 null,而游戏代码本身对 null 有兜底,激励按钮干脆直接发奖,本地把完整流程体验了一遍。
配上一段 esbuild 打包,游戏就在浏览器里跑起来了——滚轮还被顺手映射成双指捏合,桌面端能缩放视角。
【插图 02 · 02-shim.png】
这就是"适配层"这个老派工程模式的妙处:两个世界不匹配,不去改任何一边,在中间放一层翻译。 游戏仓库保持原样(还能跟着上游更新),浏览器那头也不用迁就谁,所有不匹配的脏活都收敛在那 10KB 里。
四、一个真实的坑
过程也不是一帆风顺。打包完一跑就报错:Module not found in bundle: ./levelData/level_001。
原因:游戏加载关卡用的是动态 require——require('./levelData/level_' + n),路径是运行时拼出来的。打包器只能静态分析,看到这种写法直接懵了。
解法也干净:写了个小脚本扫一遍关卡目录,生成一份显式的静态索引(每个关卡逐个列出,照样惰性加载),再让打包脚本里一个小插件把原来的动态入口重定向过去。游戏代码还是一行没动。
这类坑没有技术上的高潮,但每一个都得有人踩、有人填。
五、验收:数字说话
跑起来之后做了两层验证:
- 近 200 项单测全绿(实跑 199 项,0 失败)——游戏自带的测试套件,适配层没碰坏任何逻辑;
- 30 关无头全通——用模拟贪心玩家把 30 关从头打到尾,全部 WIN。
最后本地起个静态服务,index、bundle、shim、音效资源全部 200,浏览器打开就能玩。
【插图 03 · 03-pipeline.png】
六、这回是真·拧螺丝
回到名字。这整个事,游戏本体在拧螺丝,把它跑起来的人也在拧螺丝——查接口、写翻译、填动态 require 的坑、跑验证。没有一步是"设计一个伟大架构",全是小而具体的活。
但这恰恰是工程的大头。真正稀有的是那个 830KB 里自绘 3D 的游戏本体;而从一个仓库到"能玩",中间隔着的全是拧螺丝:适配、兜底、转换、验证。
有意思的是,这次拧螺丝的活大部分是 WorkBuddy 干的:读代码找出生死攸关的 wx 检测点、写适配层、踩坑、补测试。我的角色变成了那个指着木板说"把这几颗拧下来"的人。
游戏把"拧螺丝"做成了消遣,AI 把"拧螺丝"做成了默认工作量。 留给人的部分反而更值钱了:判断往哪拧。
想自己跑的话:GitHub 搜 nls_minigame,克隆下来装个微信开发者工具导入就能玩;懒得装的话,照着仓库里 web/README.md 三条命令,浏览器里一样开玩。