Minecraft 的画面几乎没有什么技术含量的样子:一格一格的方块,16×16 像素的贴图,没有复杂的光影,人物也是几个长方体拼起来的。第一次看到它的人,多半会觉得“这个我上我也行”。
可它偏偏是 2009 年才出现的,而且最早是 Markus Persson(Notch)一个人业余时间写的。在它之前,3D 游戏已经发展了十几年,《雷神之锤》《半条命》画面复杂得多,却没有一款游戏让玩家把整个世界一块一块地拆掉、再盖起来。
另一边,今天的电脑和手机算力早已过剩,浏览器里也有了 WebGL、WebGPU,但我们打开网页玩的 3D 游戏仍然不多,Minecraft 的完整版也不能直接在浏览器里跑。
于是有了几个问题:
- Minecraft 的玩法是从哪来的?
- 如果这个玩法这么好,早期的 3D 游戏为什么没有这么做?
- 一个几乎无限大、还能随便改的世界,内存怎么装得下?
- 为什么偏偏是 2009 年、一个人做出来的?
- 算力这么充沛的今天,浏览器为什么还是很难“原生”地跑这种游戏?自己写一个最小版本,难在哪里?
- 这样一个方块世界,为什么又成了 AI 智能体研究的考场?
本文就顺着这几个问题往下讲。写到第五个问题时,我们会真的写一个能在浏览器里玩的最小版 Minecraft,大约 500 行代码,你可以直接打开玩。
一、玩法的来源
现象:先火的是另一款游戏
2009 年 4 月 29 日,Zach Barth(后来做了《太空化学》等编程解谜游戏的 Zachtronics)发布了一款免费游戏 Infiniminer。它是一个多人方块游戏:两队矿工在地下挖矿,比谁挖到的矿石更值钱1。
有意思的是,Barth 设计的是竞技,玩家却很快发现了另一种玩法:不挖矿了,拿方块盖房子12。
不到一个月,Barth 就放弃了这个游戏。原因很朴素:它不赚钱;他没有混淆源码,代码被反编译后流传出去,外挂和魔改版泛滥,他也没有精力去修12。最后他把源码开源,停止了开发。
原因:Minecraft 补上了什么
同一年 5 月,Notch 开始写一个叫 Cave Game 的原型,它借鉴了 Infiniminer 的方块风格、第一人称视角和建造机制1。他之前做过一个叫 RubyDung 的项目,混合了《地下城守护者》《矮人要塞》和《过山车大亨》的想法3。2009 年 5 月 17 日,第一个公开版本放了出来4。
和 Infiniminer 比,Minecraft 多了两样东西。第一样是玩法上的:后来陆续加入的单人生存、合成,以及一个几乎无限大的世界,玩家有了自己找事做的理由(这是我的归纳)。第二样是生意上的:它从一开始就收钱。2009 年 6 月 13 日,还在 Alpha 阶段的 Minecraft 就开始以约 10 欧元的价格出售,不到一个月卖出了 1000 多份35。Notch 后来说:“我想,如果我不收钱,我就永远拿不到钱。”3 到正式版之前,近 200 万玩家买了 Alpha 和 Beta,收入超过 3300 万美元5。
所以玩法本身并不是 Notch 凭空发明的,它是 Infiniminer 的玩家先“玩”出来的。可如果这个玩法这么吸引人,为什么十几年的 3D 游戏史里,没有人先做出来?
二、可编辑世界的渲染代价
现象:早期 3D 游戏的世界是“死”的
回想一下 2000 年前后的 3D 游戏:门只能在剧情需要时打开,墙炸不开,地面挖不动。火箭筒打在墙上,最多留一个弹孔贴图。
原因:显卡擅长画的是“提前做好”的三角形
1990 年代末,3D 加速卡普及,它们加速的是一件事:把大量三角形画到屏幕上。
显卡是怎么画一个三角形的?它拿到的是三个顶点的坐标,然后分三步走:先把每个顶点的三维坐标换算成屏幕上的位置(顶点着色);再算出这三个点围成的区域盖住了屏幕上哪些像素(光栅化);最后给每个被盖住的像素算出颜色(片元着色)。为什么偏偏是三角形?因为三个点一定在同一个平面上,判断一个像素在不在它里面也最简单。人物、地形、汽车,任何形状在交给显卡之前,都要先拆成一堆三角形。
方块也不例外。一个方块有 6 个正方形的面,每个正方形沿对角线切开就是 2 个三角形,所以一个方块要画 12 个三角形。具体写法是:每个面给出 4 个角的坐标,再用一串索引告诉显卡哪三个点组成一个三角形,比如 0,1,2 和 0,2,3 就是把 v0、v1、v2、v3 这个正方形切成两半。本文后面写的演示就是这么做的。
注意图里左下角那句话:显卡并不知道“方块”是什么,它只看到三角形。“方块”只存在于游戏自己的数据里。游戏要先把方块翻译成三角形交给显卡,方块一变,三角形也得跟着重新生成。
当时的游戏为了在有限的硬件上跑得动,会把关卡尽量提前算好:哪些区域能互相看见、墙上的光照,都在制作关卡时预先计算并烘焙进数据里,运行时直接用(例如《雷神之锤》的 BSP 和光照贴图)。
这套办法的前提是:世界不会变。一旦玩家能随手挖掉一块墙,这些预计算的结果就全部失效,三角形本身也要重新生成,这些都得在运行时完成。对当时的硬件来说,这代价太大了。
第一次尝试:体素和局部破坏
其实早就有人想过用“方块”来表示世界。1992 年,NovaLogic 的 Voxel Space 引擎在直升机游戏《科曼奇》里用体素(volume pixel,体积像素)渲染地形,靠的是 CPU 软件渲染6。Build 引擎的作者 Ken Silverman 从 2000 年起也写过体素引擎 Voxlap7。但显卡加速的是三角形,不是体素,这条路在主流游戏里就边缘化了。
另一条路是在三角形世界里做有限的破坏。2001 年的《红色派系》用 Geo-Mod 引擎让玩家炸开岩石和建筑,被认为是第一款允许玩家非脚本化地改变关卡的游戏。但它原本计划让一切都能破坏,最后因为技术难度,以及要引导玩家走完线性关卡,范围被缩小了8。
这两次尝试分别说明了两件事:体素这种数据结构适合表示可以改的世界,但显卡不会直接画它;在三角形世界里做破坏可以,但做不到“全部”。
解决:把方块转成三角形,只画露在外面的面
Minecraft 的办法是把两者接起来:世界用方块存,画的时候把方块转成三角形。
最直接的转法是每个方块画 6 个面。但仔细想想,两个相邻方块贴在一起的那两个面,永远不会被看见。只画“旁边是空气”的面,数量会少得惊人。0fps 的一篇经典文章给过一个例子:一个 8×8×8 的实心方块堆,每块画 6 面是 3072 个四边形;去掉被挡住的面后只剩 384 个;如果再把同一平面上相邻的面合并成大矩形(贪心网格,greedy meshing),只需要 6 个9。
更关键的是第二点:世界被分成一个个区块(chunk),每个区块单独生成一块网格。玩家挖掉一个方块,只需要重建这一个区块的网格,而不是整个世界。“提前算好”的思路没有被抛弃,只是从“整个关卡算一次”变成了“每个区块在它变化时算一次”。
能画了,但一个几乎无限大的世界,光是存方块的数据,内存装得下吗?
三、无限世界的数据与生成
现象:世界大到不可能存下来
Minecraft 的世界在水平方向上可以走出几千万格。如果每个方块占 1 个字节,哪怕只存地面以下几十层,也是一个天文数字。
第一次尝试:二维高度图
最直觉的办法是不存方块,只存“每个位置的地面有多高”,用一张二维的噪声图(柏林噪声)算出来。Notch 早期的地形就是这样生成的。但他很快发现了问题:“这种方法不可能生成任何悬垂的地形”10。一张高度图上,每个位置只有一个高度,悬崖下面的凹洞、拱桥、洞穴都表示不出来。
解决:区块、按需生成和确定性
Notch 后来的做法有三个要点10:
- 按区块生成、保存、加载和渲染。当时的区块大小是 16×16×128。
- 用三维噪声当作“密度”:噪声值为负的地方是空气,非负的地方是地面。这样就能生成悬崖和洞穴。全分辨率的三维采样太慢,还会产生悬空的碎块,最后是降低采样分辨率再插值解决的。
- 确定性:地形“无论玩家从哪个方向走过来,都必须是一样的”。同一个种子、同一个坐标,永远算出同样的方块。
第三点带来一个很漂亮的推论:没去过的地方不用存,去过但没改过的地方也不用存,因为它们随时可以从种子重新算出来。真正需要保存的只有玩家改过的那些方块。世界的大小和内存的大小就这样脱了钩,玩家周围一圈区块在内存里,其余的要么还没生成,要么存在硬盘上。它的世界也因此“不是无限的,但也没有硬性边界”:走得太远,浮点精度不够,物理就开始出错,这就是玩家口中的“远境”(Far Lands)10。
技术上的几块拼图到这里都齐了。那为什么是 2009 年,而且是一个人做出来的?
四、为什么是 2009 年
这一节没有一个确定的答案,下面是我把史料拼起来之后的推断。
硬件。一个 16×16×128 的区块有 32768 个方块,每块按 1 字节算就是 32 KB,加上光照等附加数据还要更多。玩家周围加载几百个区块,光方块数据就是几十 MB,还没算网格。1990 年代末的家用电脑内存普遍只有几十到一百多 MB,到 2009 年已经是几 GB(这里的内存数字是我的估计,没有找到统一的统计)。同样的设计放在十年前,大概率跑不起来。
分发和收钱。Minecraft 用 Java 写,同一份代码能在 Windows、Mac、Linux 上跑3,早期版本甚至直接在网页里以 Java applet 的形式运行4。玩家用 PayPal 付 10 欧元买一个没做完的游戏,这在今天叫“抢先体验”,Minecraft 正是把这种模式带火的游戏之一,Steam 的抢先体验栏目要到 2013 年才上线5。一个人不需要发行商,也能靠没做完的游戏养活自己。
行业。2000 年代的大作卖的是内容:剧情、关卡、画面。一个可以随便改的世界和精心设计的线性关卡是冲突的,《红色派系》就是因为要引导玩家走完关卡才缩小了破坏范围8。大公司没有理由去做一个“没有关卡”的游戏,这件事只能由一个不需要对发行商负责的独立开发者来做。
有一个细节很有意思:Minecraft 的 Classic、Indev、Infdev 三个早期阶段都是在浏览器里玩的,直到 2010 年 7 月的 Alpha 才有了独立的客户端4。Infdev 的名字就来自“infinite”,正是无限世界加入的那个阶段。
Minecraft 最早就跑在浏览器里,为什么今天浏览器里反而很难看到这样的游戏?
五、浏览器里的方块世界
现象:官方只放出了 2009 年的版本
2019 年 Minecraft 十周年时,Mojang 在网页上重新放出了 Classic 版:只有创造模式,32 种方块,没有怪物11。完整的现代版至今不能直接在浏览器里运行。
原因:浏览器为安全付出的代价
插件时代结束了。当年网页里的 Minecraft 靠的是 Java 插件,Flash 游戏和 Unity 网页游戏也都依赖浏览器插件(NPAPI)。出于安全和稳定性的考虑,Chrome 在 2015 年 9 月彻底移除了 NPAPI 支持,Unity 也随之建议开发者改用 WebGL12。Java 写的 Minecraft 从此不能在网页里直接跑,要跑就得用 JavaScript 或 WebAssembly 重写。
图形接口来得晚。WebGL 对应的是手机上的 OpenGL ES,能力比桌面的图形接口落后一代。更现代的 WebGPU 在 Chrome 113 首发,直到 2025 年 11 月,Chrome、Edge、Firefox、Safari 才都支持了它,而且 Linux、安卓上的 Firefox 等平台还在路上13。
沙箱的限制。网页里的代码不能随意访问系统,这是浏览器的底线,也是游戏的麻烦:
- 网页不能直接开 TCP 连接。开源的网页版客户端 mcraft.fun 能连上真正的 Minecraft 服务器,靠的是先用 WebSocket 连到一个代理,再由代理去连服务器14。
- WebAssembly 的多线程依赖
SharedArrayBuffer,而在 Spectre 漏洞之后,浏览器要求页面设置“跨域隔离”的响应头才能用它15,这会影响页面嵌入第三方内容。 - 32 位 WebAssembly 最多用 4 GB 内存。64 位内存(Memory64)已在 Chrome 133 和 Firefox 134 上线,但因为每次访存都要额外做边界检查,可能慢 10% 到一倍以上16。
- 网页的存储有配额,空间紧张时可能被浏览器清掉,存档放在这里并不踏实。
没人投钱。Epic 在 2019 年 12 月的虚幻引擎 4.24 里把 HTML5 平台移出了引擎,交给社区维护。一家做网页导出工具的公司的联合创始人评价说,Epic 只支持 WebGL 1,“并不想投很多钱让它变得可用”,而且游戏动辄几个 GB,本来就是“带宽大户”17。
所以浏览器跑不好大型 3D 游戏,主要不是算力问题,而是安全模型、标准进度和商业动机一起决定的。
第一次尝试:用现成的方案
想在浏览器里玩 Minecraft,今天有几条路:
- 官方的 Classic 版,免费,但只是 2009 年的样子。
- 云游戏:游戏跑在服务器上,浏览器只收画面。能玩完整版,但它不是在浏览器里“跑”的。
- 开源的 mcraft.fun:用 TypeScript 重写的兼容客户端,Three.js 渲染,能连 1.8 到 1.21.5 的服务器14。它最接近“浏览器原生”,但不支持模组,大场景下也会吃力。
这些方案说明浏览器能跑方块世界,但都没回答我们的问题:如果只要最基础的玩法,自己从头写一个,到底难在哪?
解决:写一个最小版本
我们写了一个最小方块世界,只依赖 Three.js,用 WebGL2 渲染,源码不到 500 行。它按前面几节的思路分成五个部分。
1. 世界数据。每个区块 16×16×64,用一个 Uint8Array 存,一个方块一个字节:
const CS = 16; // 区块边长
const CH = 64; // 世界高度
const idx = (x, y, z) => x + z * CS + y * CS * CS;
2. 地形生成。四层不同尺度的柏林噪声叠在一起算出高度,种子固定,所以同一个坐标永远算出同样的地形。为了简单,这里用的是第三节说的二维高度图,所以没有悬崖下的凹洞。
function heightAt(wx, wz) {
let h = 0, amp = 1, freq = 1 / 96;
for (let o = 0; o < 4; o++) {
h += perlin(wx * freq, wz * freq) * amp;
amp *= 0.5;
freq *= 2;
}
return Math.max(1, Math.min(CH - 12, Math.floor(24 + h * 18)));
}
3. 网格生成。对每个方块的六个面,看旁边是不是空气,是才画。邻居在别的区块时去邻居区块查:
for (const f of FACES) {
const nx = x + f.n[0], ny = y + f.n[1], nz = z + f.n[2];
const neighbor = (nx >= 0 && nx < CS && nz >= 0 && nz < CS && ny >= 0 && ny < CH)
? data[idx(nx, ny, nz)]
: getBlock(cx * CS + nx, ny, cz * CS + nz);
if (neighbor !== AIR) continue; // 被挡住的面不画
// ……把这个面的四个角和颜色加进网格
}
视距是 6 个区块,加载完是 113 个区块、约 63 万个方块。每块画 6 面要 377 万个面,实际只画了约 5.7 万个,占 1.5%,左上角的 HUD 会实时显示这几个数字。为了不卡顿,每帧最多建两个区块的网格,把加载的开销分摊到多帧里。
4. 操作与碰撞。点击页面后用 Pointer Lock API 锁定鼠标,做第一人称视角。碰撞检测把玩家看成一个 0.6×1.8×0.6 的盒子,三个轴分开移动,哪个轴撞上了就把那个轴贴到方块边上。准星指着哪个方块,用 DDA 算法沿着视线一格一格地找,找到第一个实心方块和撞上的那个面,左键挖掉它,右键在那个面的外侧放一块。
5. 存档。按照第三节的推论,只存改过的方块:
const edits = new Map(); // "cx,cz" -> Map(方块下标 -> 方块 id)
生成区块时先算地形,再把这个区块的改动覆盖上去。改动以 JSON 写进 localStorage,挖一下、放一下只多几个字节。
我在没有显卡的云服务器上,用无头浏览器的软件渲染跑这个演示,约 20 帧;有显卡的电脑应该会高得多(这一点我没有实测)。
写完之后回头看,真正难的部分不在算力,而在三个地方:远处区块加载时怎么不卡(我们用了分帧,更好的做法是把网格生成放进 Web Worker);改动多了以后存档放哪(localStorage 只有几 MB,下一步该换成 IndexedDB 或 OPFS);以及多人联机时网页只能走 WebSocket。这三处,恰好都是上一小节说的浏览器限制。
一个能随便改、规则又很清楚的世界,还有什么别的用处?
六、方块世界与智能体
现象:Minecraft 成了 AI 的考场
过去几年,Minecraft 几乎成了 AI 智能体研究的标准环境。原因和前几节说的特点直接相关:世界是开放的,没有固定目标,适合考察“自己找事做”;规则是离散、明确的,方块就是方块,合成表就是合成表;世界可以被改,智能体做了什么会留下看得见的痕迹;网上还有海量的游戏视频可以学。
第一种接法:看屏幕,按键盘
2022 年,OpenAI 的 VPT 让模型像人一样只看画面、用键盘鼠标操作。他们先用少量带按键记录的录像训练一个模型,给大约 7 万小时网络视频补上“这一帧按了什么键”的标签,再拿这些视频预训练。最后在 2.5% 的 10 分钟回合里做出了钻石镐,而熟练的人类平均要 20 多分钟、约 24000 个操作18。
这种接法最通用,但也最难:模型要从像素里理解世界。
第二种接法:直接调用接口
2023 年的 Voyager 换了一个思路:不让 GPT-4 去按键,而是让它写代码。游戏提供一套 JavaScript 接口,GPT-4 自己给自己出任务,写代码完成任务,根据报错修改,验证通过的代码存进“技能库”,下次遇到类似任务直接取出来用。和之前的方法比,它收集到的物品种类多了 3.3 倍,解锁科技树的关键节点快了最多 15.3 倍19。
2024 年,Altera 的 Project Sid 把 10 个到 1000 多个智能体放进同一个 Minecraft 世界,观察到了分工、集体规则的制定和修改,以及文化和宗教在智能体之间的传播20。
这种接法后来也走到了普通人手里。有了 MCP(模型上下文协议)之后,社区做了 Minecraft 的 MCP 服务器,把移动、挖、放、合成、聊天这些动作暴露成工具,在 Claude 这样的助手里用一句话就能指挥游戏里的角色盖房子21。
我们的演示里也有一行类似的东西:
window.voxel = { player, getBlock, setBlock, stats, meshes };
它本来是给调试用的,但你在浏览器控制台里调用 voxel.setBlock(...),和一个智能体调用 MCP 工具,本质上是同一件事:通过接口读写世界。网页游戏如果把这类接口规范地暴露出来,就能被智能体直接操作。
第三种接法:让模型本身成为游戏
2024 年 10 月,Decart 发布了 Oasis:没有游戏引擎,一个在 Minecraft 视频上训练出来的模型根据你的键盘鼠标输入,实时生成下一帧画面22。它看起来像 Minecraft,但有一个致命问题:模型很快会“忘掉”地形,你转一圈回来,眼前的景色已经变了22。
这正好回到了本文的主线。Minecraft 的核心不是方块的样子,而是一个能被记住、能被持久修改的世界。第三节里区块、种子和改动记录解决的就是这件事,而这恰恰是今天的生成式“世界模型”最欠缺的。
七、回到起点
现在可以回答开头那个“这个我上我也行”了。Minecraft 看起来简单,是因为它把复杂度藏在了看不见的地方:画面上是方块,底下是一套让世界可以被随便修改、又不会撑爆内存的数据结构和算法。方块的画风也不只是省事,它让每个玩家盖出来的东西和官方的内容一样“好看”(这是我的理解)。
它来得晚,是因为这件事需要好几个条件同时成立:显卡和内存够用,Java 让一个人写的程序能跑在所有电脑上,网络让一个人能直接把没做完的游戏卖给玩家。而当年它能直接在浏览器里运行,今天却要重写才能回去,这中间隔着的是浏览器十几年的安全收紧。
小结
- Minecraft 的玩法从哪来? 来自 Infiniminer 的玩家。Infiniminer 设计的是竞技,玩家却拿它盖房子;作者因为不赚钱、代码失控而放弃,Minecraft 补上了生存、无限世界和收费。
- 早期 3D 游戏为什么不这么做? 当时的性能来自“提前算好”的静态关卡,可修改的世界会让预计算全部失效。体素渲染输给了显卡的三角形路线,局部破坏也做不到“全部”。Minecraft 把方块转成三角形、只画露在外面的面、按区块重建网格,才让两者接上。
- 这么大的世界怎么装得下? 按区块、按需、确定性地生成,没去过和没改过的地方都不用存,存档只记改动。
- 为什么是 2009 年? 内存够了,Java 能跨平台甚至跑在网页里,预售让一个人能养活自己,而大公司没有动机去做没有关卡的游戏(推断)。
- 为什么今天浏览器很难跑这种游戏? 不是算力,而是插件被移除、WebGPU 刚刚普及、沙箱对网络线程内存存储的限制,以及引擎厂商不投入。但只要最基础的玩法,500 行代码就够了,可以直接试试。
- 为什么它成了 AI 的考场? 开放、规则清楚、可修改。智能体可以看屏幕操作,也可以调用接口操作;用模型直接生成游戏画面的尝试,卡在了“记住世界”这件 Minecraft 早就解决了的事情上。
参考资料
Footnotes
-
Wikipedia:Infiniminer ↩ ↩2 ↩3 ↩4
-
Crosscut:The local origins of Minecraft: A conversation with indie gamer Zachary Barth,2015 ↩ ↩2
-
TechRadar:The history of Minecraft ↩ ↩2 ↩3 ↩4
-
Lost Media Wiki:Minecraft (lost early builds of Java game; 2009-2013) ↩ ↩2 ↩3
-
Wikipedia:Early access ↩ ↩2 ↩3
-
Wikipedia:Voxel Space ↩
-
Wikipedia:Ken Silverman ↩
-
Wikipedia:Red Faction (video game) ↩ ↩2
-
Mikola Lysenko:Meshing in a Minecraft Game,0fps,2012 ↩
-
Markus Persson:Terrain generation, Part 1(ModDB 转载) ↩ ↩2 ↩3
-
Engadget:Minecraft Classic browser,2019 ↩
-
Unity Blog:Web publishing following Chrome NPAPI deprecation ↩
-
web.dev:WebGPU is now supported in major browsers,2025-11-25 ↩
-
Minecraft Web Client 文档:Introduction;源码 zardoy/minecraft-web-client ↩ ↩2
-
web.dev:Making your website “cross-origin isolated” using COOP and COEP ↩
-
SpiderMonkey Blog:Is Memory64 actually worth using?,2025-01-15 ↩
-
The New Stack:A New Tool for Unreal Engine Developers to Export to the Web ↩
-
OpenAI:Learning to play Minecraft with Video PreTraining,2022 ↩
-
Wang G, et al. Voyager: An Open-Ended Embodied Agent with Large Language Models. arXiv:2305.16291, 2023 ↩
-
Altera.AL. Project Sid: Many-agent simulations toward AI civilization. arXiv:2411.00114, 2024 ↩
-
TechCrunch:Decart’s AI simulates a real-time, playable version of Minecraft,2024-10-31 ↩ ↩2