怎么用 48×48 网格做出“化学引擎”
核心想法只有一句:不要给每个物体写“被火烧了会怎样”,而是让元素(火、水、电、风)只和“材料属性”打交道,再让元素之间按固定规则互相影响。剩下的涌现,都是规则组合出来的。
三条规则(GDC 2017)
元素 → 材料 ✓
火遇到有燃料、够干的格子就点燃;电遇到导体就传导;水让格子变湿。元素可以改变材料的状态。
元素 → 元素 ✓
水浇灭火,热融化冰,冰水变成可导电的水,火产生上升气流。元素之间也有固定的互动规则。
材料 → 材料 ✗
草不能直接点燃旁边的草。燃烧的格子只“放热”,邻居是否着火,由它自己的温度和属性决定。这条禁令让系统保持可预期。
数据布局
世界被切成 48×48 个格子,每个格子只是几个平行数组里的一个数(Float32Array,下标 i = z*48 + x):
temp温度,环境 20,燃烧可达 900wet湿度 0..1,水格恒为 1fuel燃料 0..1,草多木少,石头为 0burn燃烧强度,渲染火焰用ash灰烬量,渲染焦黑用ice冰块质量charge电荷,沿导体逐格衰减传递steam蒸发 / 淬火强度,渲染蒸汽用规则流水线
事件点火 · 下雨 · 通电 · 放冰
→
60Hz 规则 tick燃烧 · 传热 · 雨水 · 融冰 · 导电 · 气流
→
状态数组temp / wet / fuel / …
→
Three.js 渲染只读数组,不参与计算
装进你的游戏
1. 模拟与渲染分离
模拟是纯 JS、固定步长、确定性(不用 Math.random),可以在 Node 里测试。渲染每帧只读数组,所以同一份模拟能驱动预览、录屏、回放。
2. 规则只看属性
规则里不要出现“如果是草”。写成“fuel > 0.05 且 wet < 0.3”。新增一种材料,只需要给它一组属性,不用改任何规则。
3. 先网格后三维
先在二维网格里把规则跑顺,再用空间查询把三维物体映射到格子:位置 → 格子下标 → 读属性 → 写回事件。别一上来就做三维流体。
源码查看
下面直接读取 src/sim.js 里的真实代码,每条规则都是一个独立的小函数。
加载中…
来源与说明
- GDC 2017:《Change and Constant: Breaking Conventions with The Legend of Zelda: Breath of the Wild》,Hidemaro Fujibayashi、Satoru Takizawa、Takuhiro Dohta。
- 日文报道:GAME Watch · game.watch.impress.co.jp/docs/news/1047403
这是一个简化的教学原型,是原创实现,不含任天堂代码:用网格近似空间,没有流体模拟,电荷只靠相邻格传导,数值是为了好看和好懂调出来的,并非游戏内真实参数。