改一个数字,等场景重建,按 W 往前走,走到那个位置,抬头看那排书架是不是浮在半空。
这是我最近干得最多的一件事,一天几十次。
九月底翻到 Mika Flowers 发在 dev.to 的一个项目:把整个 DEV.to 塞进一个第一人称 3D 图书馆。Next.js 加 Three.js,没上游戏引擎,没上可视化场景编辑器,所有物体的位置都是代码里的一串数字。文章从 Forem API 实时拉,书脊上是真封面,WASD 走,不滚动。
一开始这项目几乎全靠 AI 辅助的 vibe coding 搓出来。
读到这儿,各位心里那个"然后呢",我猜已经有数了。
【灯光渐暗,一个语言模型正对着一片虚空,极其自信地摆放书架】
作者原话大意:项目小的时候,重新生成一下 AI 的回复还能糊过去;等目录接进来几千篇真实文章,糊不过去了。
为什么糊不过去,这才是我想说的。
坐标这东西,没有"看起来对"这回事。
你让 AI 把卡片往左挪 30 像素,它挪了,刷新一看像是对的,收工。你让 AI 把一排书架往左挪 0.3 个单位,它挪了,代码工整,diff 干净,注释都给你补好了。然后你按着 W 走过去,发现那排书架一半插在墙里,另一半悬在空中——而 AI 在你迈出这一步之前,永远不可能知道自己干错了什么。
它没有腿。
它不进场景,不站到那个位置,它看到的只是自己写下的数字。而数字本身,永远是对的。
这大概是我今年最踏实的一条认知:AI 能替你写完的东西,和你必须替它验证的东西,是两码事。
插播一段这项目的来历,因为它跟后面要说的是同一件事。前身叫 Oniria,一个第一人称 3D 梦境日志,记忆是发光的球体,球体之间用纽带连着,能沿着纽带飞;后来加了电影感灯光、空间音频、递归梦境潜入、记忆衰减,还有一个俯瞰全部梦境历史的 Observatory 模式。作者在那一版里比 main 分支多出 197 个提交,代码约 1.3 万行,然后开始重新想:这个 hackathon 的范围,是不是有点大。
最后他砍成一个 90 秒的体验,其余想法全部冻结。
"比 main 多 197 个提交",这句话本身就是一篇完整的恐怖小说。
回到图书馆。真正离谱的不是坐标,是书架会自己动。文章是分页流式进来的,一批批地加,书架的位置又互相依赖——在普通网页里,列表多渲染一次,谁都不当回事。可在第一人称空间里,这意味着你正站着的地方,下一秒可能变成一排书架。
你人没动,世界在你脚底下重排了一遍座位。
作者的说法大意是,导航变成了布局的副产品,而不是这个世界的保证。这句我读了两遍。它不是运维事故,它是一种哲学事故。
后来他是这么解的:给整个图书馆定一条权威路径,把逻辑上的 bay 索引换算成世界坐标,书架、雾、天际线、能量通道、相机边界全部从同一条固定路线上采样;书架本身改用带种子的确定性函数来摆,输入是书架 ID、近似 bay、路径侧、抖动。
翻成人话:书架不再负责定义世界,它只挂在世界上。
然后这一类 bug 一次性没了。继续往下流文章,也不危险了。
我看到这儿有点想笑,因为压根不是什么高深架构,这就是"别把可变状态当权威"。写在面试题里人人都会答。真到 3D 里,你得先被自己的地板传送一次,才想得起来。
另一个事故我特别有共鸣:运行时抛了 zoneRef.current is not a function,代码九百行出头。追下去,是 zoneRef 在一个依赖 currentSection 的 useEffect 里赋值,而动画循环在那个 effect 第一次执行之前就已经起跑了。
stale closure。渲染循环比 effect 先跑,读到的是一份过期的自己。
这块我一百个支持作者的判断:这种活儿 AI 帮不上。你可以让它把那个 effect 重写十遍,每一版都工整得能进教材,然后那个报错在原地等你。因为问题不是"这段代码怎么写",问题是"谁先跑、谁后跑、谁读到的是几帧之前的那份自己"。
AI 对"几帧之前"没有概念,它没有时间感。它只有上下文窗口里的顺序。
说个我自己的实情:我现在开调试的第一步,已经悄悄变成了把报错整段粘进对话框。这个动作取代"先把报错读一遍",用了不到一年,而且我第一次意识到这事的时候,居然没觉得哪里不对。
这才是最吓人的地方,比书架会动吓人多了。🫠
评论区也挺好看。有人建议作者先让 AI 生成一套调试覆盖层——坐标轴、包围盒、书架位置标签、几个能拖的滑杆,还建议在不匹配的时候记录信息而不只是记数值。
我第一次读这条没看懂,第二次读,觉得是那批评论里最值钱的一条。
它的方向不是"让 AI 帮你把书架摆对",是"让 AI 给你造一副眼镜,让你能看见它刚才摆了什么"。这俩差得挺远。前者迟早撞墙,后者能撑很久。
还有人指出,"路径作为权威坐标"这条原则能推广到流式目录、分布式缓存、代理的上下文窗口——凡是"位置由可变状态派生"的场景都算,那个 zoneRef 就是一个微缩版的竞态条件。
他是对的。
而这才最气人:一个 3D 图书馆里"书架别乱跑"的经验,能在分布式系统里原样复述一遍。你就说这行干久了,是不是走到哪都在修同一个 bug。
顺带一提,另一位在 Three.js 场景图里撞过同样瓶颈的人说:20 个对象一切正常,2000 个对象帧预算直接崩,最后还得回去读渲染器源码。
这条路不止一个人走过,我怀疑是所有走过的人。
这版还有个设计细节我挺喜欢:离得近的书架会亮起来、封面被唤醒,远处的暗下去。作者说,在一个 3D 档案里,距离和亮度是唯一可用的层级提示,这一点比他预想的重要。
确实是。在信息流里你能用"第几篇""多少赞"分层,到了 3D 里只剩下远近和明暗。降维打回原形,一点脾气都没有。
项目本身没做完。作者还在定目录加载量的上限,怕远处的书架白烧帧;雾和天际线调过了,但没在弱 GPU 上验证。他自己说,大概还有一堆 zoneRef 一样的 bug 在等着相遇。
有人留言希望 DEV.to 把这个做成官方可选功能,作者回了一句,大意是:看到有人真的想用,那些调试就值了。
这句我信。
(友情提示:本图书馆无限流,书架会在你脚底下重排,路径是唯一权威。走之前,记得把 W 松开。)