下载 App
《万象纪》开发日志 #1|从一个想法到落地
修改于10/08589 浏览综合
写在前面
《万象纪》是一款上帝模拟游戏。你从猿人时代开始俯瞰一座岛,看一群猿人觅食、迁徙、繁衍,偶尔伸手干预:降一道雷、下一场雨、托一个梦。
如果你什么都不做,或者只做些“自然”的干预,这个文明会像真实的人类历史一样,一步步走到现代社会。 如果你改变了世界的法则,比如开辟灵脉、引来魔力潮汐、降下星陨、抽走某种资源,文明可能会长成蒸汽朋克、修真世界、魔法世界、赛博朋克,或者某种连我们自己都没预料到的混合形态。
我们给这款游戏写下的一句话是:
你创造了世界,却无法决定它会成为什么。
这一篇讲的是它怎么从一个想法变成一个能跑的世界:我们怎么理解主题、核心系统怎么设计、地基怎么打,以及美术上绕的一个大弯。
一、我们怎么理解「涌现」
1. 第一个方案,被我们自己否掉了
拿到「涌现」这个主题,最顺手的解读是“简单规则相互作用,产生复杂结果”。我们最初的方案就是这个思路:做一套彼此影响的系统,让玩家看到连锁反应。
讨论到一半,我们发现了问题:照这个说法,几乎所有系统驱动的游戏都“符合主题”。这其实等于没有解读,“涌现”只是贴在引擎上的一个标签,玩家感受不到。
我们还排除了两种常见的误读:
- 因果链不是涌现。 A 导致 B,B 导致 C,这是蝴蝶效应。每一环都能单独解释,整体并没有出现新东西。
- 数量累加不是涌现。 “种满 30 朵花”这种目标,只是部分加在一起,整体没有任何部分以外的性质。
2. 两道检验题
最后我们给自己定了两道检验题,后续所有设计都要拿来对照:
- 把“涌现”的部分换成脚本,游戏还成立吗? 如果还成立,主题就只是装饰。
- 玩家面对的那个“整体”,是否拥有任何部分都没有、也没有任何部分想要的性质?
中文的「涌现」还有一层画面感:东西从底下涌上来,然后显现出来。我们希望玩家真的能“看见”这个过程。
3. 我们的答案:让“文明形态”成为涌现的主体
在《万象纪》里,没有任何一个猿人“想要”蒸汽朋克。每个个体只会做很小的事:发现一个想法、教给邻居、在几种做法里挑更好用的那个、然后老去和死亡。“蒸汽朋克”是整个文明的性质,不属于任何一个人。
玩家是上帝,但上帝不能选路线,只能改变条件:天气、资源、世界法则。路线从个体之间想法的传播里长出来,游戏是事后才“认出”它是什么的。
拿第一道题检验:如果把文明形态换成一个“选择科技树”的菜单,这个游戏就不存在了。这正是我们想要的。
二、核心设计:想法网络

想法是文明演化的最小单位,涵盖技术、制度、信仰和异术。整个系统只有四条规则:
| 规则 | 内容 |
|---|---|
| 发现 | 个体掌握了全部前置想法、又满足环境条件时,按概率发现新想法。发现者会成为有名字的人物,记入编年史 |
| 传播 | 通过接触和教学传给 3 格内的邻居。有了“语言”之后传播快 4 倍。孩子继承父母的想法,但会丢掉一些 |
| 失传 | 掌握某个想法的人全死了,它就消失了。黑暗时代由此自然出现;有了文字和典籍,失传的想法还能被重新发现 |
| 竞争 | 同一个“生态位”里的想法相互竞争,每个人只践行其中最好用的一个。占上风的那个决定了文明在这方面的样子 |
举个例子,“用火”这个想法的发现条件是亲眼见过火。所以一道落在树林里的雷,可能让目睹大火的猿人学会用火;“万物有灵”需要目睹过雷电、灾害或死亡。玩家的神力不直接给文明“加科技”,而是制造条件。
生态位:形态是胜出者的组合
| 生态位 | 正常历史 | 非常路线(举例) |
|---|---|---|
| 能源 | 人力 → 火 → 水车风车 → 蒸汽 → 电力 | 灵气阵法、魔晶、灵炉、魔导机关 |
| 治理 | 群居 → 部落 → 王权 → 封建 → 现代政体 | 宗门、法师学院、巨型企业 |
| 信息 | 语言 → 文字 → 印刷 → 电报 → 计算机 | 符文、神识、水晶网、差分机 |
| 交通 | 步行 → 车轮 → 航海 → 铁路 → 汽车 | 御剑、飞艇、传送法阵 |
文明形态就是各生态位胜出者的组合。游戏持续计算各种形态的接近度,某个形态够高并维持一段时间,就会被“认出”。两种形态都有一定接近度、但都没到认定线时,游戏会自动合成名字,比如“蒸汽修仙”“魔导工业”“灵气复苏”。另外还有几个“混合想法”,比如灵炉 = 阵法 + 蒸汽机,让混合形态在画面和玩法上也真的成立。
正常路线是“吸引子”,不是脚本
在地球式的世界里(没有异象,资源分布合理),前置条件和环境条件会让发现顺序自然接近真实历史。每一局的节奏、发明者、哪个聚落领先、什么时候出现黑暗时代都不一样,但大方向会收敛到现代社会。
变体只来自非常干预,而且都能追溯到具体原因:
| 成因 | 例子 |
|---|---|
| 资源条件改变 | 抽走铜和石油 → 电力长不出来 → 蒸汽技术越钻越深 → 蒸汽朋克 |
| 世界法则改变 | 开辟灵脉、引来魔力潮汐 → 新的想法家族出现,在各生态位里和正常想法竞争 |
| 社会结构失衡 | 神只偏爱一群人,再加上星陨带来的早熟技术 → 巨型企业 + 义体 → 赛博朋克 |
神力与信仰的张力
- 常规神力 7 种:雷霆、降雨、干旱、丰饶、瘟疫、地动、托梦。它们不改变世界法则,只影响节奏和局部历史。托梦也只能植入“前置已满足”的想法,可以加速历史,但不能跳过历史顺序。
- 非常神力 5 种:地脉改造、开辟灵脉、魔力潮汐、降下星陨、神眷。它们改变法则,才会引出变体。
神力消耗信仰,信仰来自祈祷和被回应的祈愿。这里有一个我们很喜欢的、不需要额外设计的取舍:文明越相信科学,祈祷就越少,神就越弱。正常路线走到后期,上帝只剩下托梦这种很微小的手段;修真、神权这类路线能维持信仰,但文明也就走上了另一条路。
设计支柱
| 支柱 | 检验方式 |
|---|---|
| 文明是涌现的 | 把涌现部分换成脚本,游戏就不成立 |
| 正常是基线,变体有因 | 不干预时 50 个随机世界的自动回归测试 |
| 每次出手都有回响 | 0.5 秒内有可见反馈,长期后果可以追溯 |
| 一局有终点,世界无尽头 | 主线有结算,沙盒可以无限演化 |
| 历史被看见 | 玩家不看编年史,也能说出这一局发生了什么 |
最后一条决定了很多美术和镜头上的投入:涌现如果只发生在数据里,玩家是感受不到的。
三、技术地基:一个能“批量看历史”的模拟器
1. 为什么要把模拟和画面彻底分开
涌现系统最大的麻烦是难以调试、难以平衡。“这一局为什么没进入部落时代?”这种问题,靠玩几遍是答不出来的。我们需要一次跑 50 个世界,统计“不干预时有多少能走到现代”;还要验证每一种形态都能被某种干预路线达成。
所以从第一天起我们就定了几条硬规则:
- 模拟层是纯数据。sim/ 和 data/ 不能引用渲染引擎、DOM、界面或音频,可以在命令行里无头运行。
- 画面只读不写。渲染层只读取模拟的快照和事件;玩家的任何操作都必须通过 sim.apply(command) 进入模拟。
- 严格确定性。随机数分成世界生成、模拟、事件、命名、特效几条独立的流;模拟代码里禁止出现 Math.random、Date.now 这类东西,依赖规则测试会自动扫描并拦下。同一个种子加同一份命令日志,可以逐位重放出同一段历史。
无头跑一个 10 分钟的世界只需要几秒钟。下面是种子 7 每分钟一行的时间线(节选):
textMinute | Year | Era | Population | Settlements | Animals | Deaths(age/starvation/wolf) 0 | 0 | T0 原始 | 12 | 2 | 143 | 0/0/0 1 | 15000 | T0 原始 | 60 | 2 | 210 | 30/0/2 2 | 30000 | T0 原始 | 90 | 2 | 129 | 136/0/3 3 | 45000 | T0 原始 | 71 | 2 | 96 | 275/0/3 4 | 60000 | T0 原始 | 50 | 2 | 91 | 387/0/3 5 | 75000 | T0 原始 | 86 | 2 | 84 | 476/0/3 …… 10 | 90600 | T1 部落 | 87 | 2 | 71 | 1133/0/3
人口有起有落,没有人饿死,被狼咬死了 3 个;鹿、羊、狼的数量也随着狩猎和繁殖起伏。这样的时间线,就是我们调参时每天要看的东西。
2. 世界生成
- 128 × 128 格的岛屿或群岛,高度 0 ~ 15 阶。噪声函数是自己写的。
- 山脉用域扭曲的脊状多重分形生成连绵的山脊链,而不是一个个同心圆锥;沿山脊和海岸有断崖,沙漠里有平顶台地,河流把沿途地面下切一阶形成河谷,遇到高差就成了瀑布。
- 资源按“地球式”规则投放:燧石、黏土在出生点附近,铜锡在丘陵,铁在山地,煤在中距离,石油在远处或沙漠,硝石在干燥地带。
- 生成后要做校验:所有关键资源必须能从出生点走过去(只考虑高差不超过 1 阶的移动),不满足就换下一个种子重来。
3. 性能预算
模拟:300 个个体约 0.5 ~ 0.7 ms/tick(1 倍速每秒 10 tick)。画面:1080p 中画质约 5.4 ms/帧(Apple M4 测得)。画质档位会在启动时按真实画布测速自动选择,掉帧时自动降档。
四、美术:从“像素画”到 HD-2D
一条硬约束
我们给自己定了一条规矩:游戏里所有美术和音频都必须用代码做出来,不用任何外部的美术、字体和音频素材。能程序化的就程序化,比如地形、建筑、树、石头、云和所有贴图;人物、动物和图标则用“文本像素画”在代码里一个像素一个像素地写出来(后面会展示)。
第一版:整帧低分辨率,被推翻了
最初的方案是很多像素风 3D 游戏用的路子:把整个画面渲染到很低的分辨率(480×270),量化到调色板,加描边和抖动,再整数倍放大。

单看截图并不差,但我们给阶段 0 设的闸门是“画面对味”,这一版没有通过:
- 地形在整帧像素化之后,一眼看上去像 Minecraft;
- 抖动在大面积水面和远景上变成了噪点;
- 最关键的是,它不像我们心里那张“放在桌上的精致沙盘”。
改成 HD-2D:像素住在贴图里,光影是高清的
我们改成了 HD-2D 风格,也就是《八方旅人》《三角战略》那种“立体沙盘”的做法:
- 像素:所有贴图(地面、岩壁、屋顶、树叶)和所有角色、动物、小物件、图标都是像素画,全场景统一为每格 32 个像素;
- 高清:几何边缘、光照、阴影、环境光遮蔽、景深、辉光、体积光、雾、水面反射,都按屏幕原生分辨率平滑渲染。
为此重写了整条渲染管线:HDR + MSAA;像素贴图用“最近邻 + 像素边缘抗锯齿”采样,任何缩放和角度都不闪、不糊;贴图自动生成法线;半兰伯特光照,亮部偏暖、暗部偏冷;PCF 软阴影;按高度场解析计算的环境光遮蔽;云影;体积光和云隙光束;辉光;带圆形散景的景深(神视下是移轴效果);ACES 色调映射加调色 LUT;暗角和极轻的颗粒。
第二次“像 Minecraft”:倒角地形
HD-2D 第一版出来以后,光影对了,但地形还是一格一格的方块。问题出在几何上:模拟用的是格子,画面也老老实实画了格子。
我们的解决办法是倒角地形:每个格子拆成一个菱形加四个角三角形,凸角切掉、凹角填平,于是海岸线和梯田边缘变成了 45° 的折线。模拟层的格子高度完全不变,点击拾取也用同一个倒角模型,所以“点到哪就是哪”。

下面四张图是同一座岛在这一阶段的变化:

左上:第一版低分辨率;右上:HD-2D 第一版;左下:倒角地形;右下:现在。
复审,再返工
每个阶段结束都有一次复审:截图矩阵(3 个种子 × 3 档缩放 × 多个朝向 + 低角度环绕镜头)、手感实测、性能测量,最后列一张问题清单。阶段 0 的复审结果是有条件通过,留下了三个美术问题:

- 草地像波点布:草地贴图里的花、秸秆和亮斑排得太均匀。改成蓝噪声散布的草簇,着色器里逐个贴图像素随机旋转混采,打散 4 格一次的平铺感。
- 岩石像砌墙:岩壁上规则的缝看起来像砖墙。改成大块明暗 + 断续节理 + 苔藓渗入,露岩只出现在部分平台边缘。
- 河流断成水坑:原来按“单一高度 ± 0.75”判断哪里画水,跨台阶的地方上下两层都不画。改成每段河道记录它跨越的高度区间,沟谷满水,跨阶处画瀑布和落水白沫。

这一阶段最大的教训:风格要尽早用真实画面去对齐。“像素风”三个字,每个人脑子里的画面都不一样。
神视:手感也是画面的一部分
上帝视角(我们叫它“神视”)是一台透视相机,视场角 28°,仰角随缩放在 40° ~ 58° 之间变化,画面上下带移轴虚化,像在看一张桌上的沙盘。几个手感上的细节花了不少功夫:
- 精确抓地:右键或中键拖动时,按下的那块地会一直“粘”在光标下面,不会越拖越偏;拖到地图边缘时地面会在光标下打滑,而不是把镜头拖出地图。
- 以光标为锚点的连续缩放:滚轮、触控板和捏合都以光标所指的位置为中心缩放,并带一点惯性;松开拖动后镜头也会轻微滑行。
- Q / E 旋转 45°:用 0.3 秒的平滑补间转过去,连按也不会顿挫。

五、让世界“活”起来
阶段 1 的闸门是“小人是活的”。

1. 个体与聚落
- 屏幕上最多 300 个小人。人口超过上限时,同一聚落的小人会合并,一个小人代表的人数变多,画面数量和模拟开销都有上限。
- 每个个体有随机、可遗传的性格:好奇(更容易发明)、虔诚(更容易祈祷)、保守(更难接受新想法)、勇敢(更愿意迁徙和冒险)。
- 个体会觅食、劳作、繁衍、迁徙、逃跑;鹿、羊、狼有自己的生态,狼会追猎猿人。
- 聚落有有限的食物,吃完了就整体搬迁;人口太多时,会有勇敢的人带一部分人出去另立营地。
2. 文本像素画:在代码里画画
所有角色都是这样“写”出来的。下面是猿人的头部,每个字符是一个像素:
ts// 猿人头部(朝右):毛发帽檐受左上方光照,眉骨突出,浅色面部、眼睛、耳朵和口鼻 const HEAD = part(` ...hhhh.... .hhiiihhh.. hhiihhhhhh. hhhhhHHHHHh hhhhSsssess HhhhssssTTTT .HhhhhsSSSs. ..HhhhSsss.. `, { head: [5, 1] }); // 头顶挂点:头饰会挂在这里
h / H / i 不是固定颜色,而是毛发槽位的中间色、暗色和亮色;s / S / T 是皮肤槽位。图集里存的是“槽位 + 偏移”的编码,着色器按每个个体的外观替换成具体颜色。所以同一套动画可以表现 4 种肤色、8 种发色、10 组服饰配色,不用重画任何一帧。外轮廓自动生成,头、手、背各有挂点,用来挂羽毛、骨饰、石斧和火把。

这些精灵放进 3D 场景以后,会和地形一样受光:落在阴影里会变暗,被篝火照亮,逆光时有一圈轮廓光,脚下有接触阴影。


几个“让小人可读”的细节:
- 先解析颜色,再做滤波。如果先缩放再换色,相邻的槽位会串色。所以图集存编码,着色器先解析出颜色,再在贴图像素空间做精确的盒式滤波,近景不闪,中景不跳。
- 拉远时略微放大。中档缩放下,16×24 的小人只有十来个像素高,读不出动作。我们让“一个精灵像素不小于 0.9 个屏幕参考像素”,最多放大约 1.45 倍。这是全场景像素密度统一规则里唯一的例外:可读性优先。
- 表情是气泡。11 种表情画在 13×13 的白色气泡里,最后叠在画面最上层,在任何地表上都看得清。
- 跳格变成走路。模拟里个体是一格一格跳的,表现层把它平滑成连续的行走,同一格的人会微微散开,上下台阶也是平滑的。

3. 植被与大气
树是 3D 树干 + 几十张像素叶片卡片簇成的树冠。卡片始终正对镜头,光照用整个树冠的球面法线,所以从任何角度看都饱满,又有平滑的体积明暗;逆光时叶缘透光,风吹时成片摆动。阔叶、针叶、棕榈、枯树、焦树一共 16 个变体,各有远近两档模型。所有模型展开后放进一张数据纹理,不管有多少变体,整片森林的树和小物件只要 3 次绘制调用(另加 3 次阴影)。

几条我们刻意做的取舍:
- 树比真实比例小:阔叶树大约是人的 2 ~ 3 倍高。俯视时,真实比例的树会把地面和小人全挡住。
- 人走到树下,树冠会变成网纱,而不是整棵消失。
- 最近档缩放下,每一格陆地都有东西可看:30 种地表细节(蘑菇、蕨、芦苇、睡莲、贝壳、兽骨、仙人掌……),草和花随风成片起伏。
- 环境小生命:鸟群在森林和水面上盘旋,有人靠近会惊飞;草地上有蝴蝶;有火的营地会冒炊烟。


4. 路是走出来的
这是阶段 1 里我们最喜欢的一个“小涌现”。
模拟里有一张足迹场:个体每走过一格,这一格的足迹 +1,并随时间缓慢衰减。足迹多了,草先被踩倒、压黄,然后露出土粒,最后连成边缘不规则的土路。没有任何规则在铺路,路完全是猿群自己走出来的。营地搬走以后,旧路会慢慢退化成小径,但“这里住过人”的痕迹还在。(之后掌握相应的想法,土路会升级为石板路和柏油路。)


5. 建筑
建筑也是代码拼出来的:一套模块库(墙体、7 种屋顶、塔身、烟囱、门窗、台阶、栅栏、城墙段,以及会转的风车、水车、齿轮)→ 建筑规格 → 几何缓存 → 每种规格一次实例化绘制。每种建筑有 2 ~ 4 个变体,再加上随机朝向、缩放、镜像和色调抖动,同一个村子不会像复制粘贴。
目前完成了原始时代和部落时代两套:兽皮帐篷、岩洞(靠悬崖时自动出现)、骨堆、篝火圈;茅草圆屋、粮仓和田、图腾柱、村落篝火、编篱栅栏和寨门,以及奇观巨石阵。另有 19 种聚落小物件(柴堆、陶罐、晾肉架、兽皮架……)和会生长的麦、稻。

建造有过程:先搭木架,墙体自下而上填满,屋顶最后盖上,扬起一阵尘土。时代升级时,旧建筑沉下去,新建筑回弹升起,按到聚落中心的距离一圈一圈地扩散。

(这一段是调试预览:我们用开发工具摆了一个营地,并提前把它切到部落时代。真实游戏里,聚落的时代由文明自己推进,建筑再跟着时代自动升级,这部分接线还在开发中。)

建筑的外观跟着聚落掌握的知识走:掌握了图腾崇拜才会立图腾柱,靠着悬崖才会有岩洞。其中我们特意做了一个决定:
火要“学会”才有。 聚落还没学会用火的时候,篝火圈是冷的,不冒烟,夜里门洞也是黑的。
这样,“第一把火”就会成为一个看得见的转折:某一天,一道雷点燃了树林,有猿人看见了;又过了许多年,营地里第一次亮起了火光,炊烟升起,夜晚不再漆黑。这个瞬间不是脚本触发的,它是文明自己走到的。


六、界面:一部摊在沙盘边上的史书
界面我们想要的感觉是“一部摊在沙盘边上的史书”:半透明的墨色玻璃面板、传统书籍版式里的双线边框、像素回纹角花、宋体标题、像素位图数字。和画面的逻辑一样,界面是高清的,像素只出现在图标、数字和角花上。

- 所有颜色都从游戏的调色板里取;信仰永远是黄铜色,危险永远是红色。
- 强调色会随文明形态渐变:修真偏玉色,魔法偏紫,蒸汽偏黄铜,赛博偏霓虹。
- 7 种常规神力的图标是自然界的物象;5 种非常神力的图标是裂开的禁忌封印,里面的东西正在发光、冲破封印。
- 面板都是浮动的小块,不做通栏。我们给自己定了硬指标:全部面板打开时,世界画面仍有 70% 以上可见(实测 1280×720 下为 71.8%,2560×1440 下为 72.6%)。

七、我们怎么工作
文档先行。 写第一行代码之前,我们先写了五份设计文档:设计总纲、内容数据、技术设计、美术与音频规范、开发计划(现在加起来约 2800 行)。后来每一次设计变更,都要先改文档、在“偏差记录”里写下原因,再动代码。比如“美术改为 HD-2D”“火要学会才有”“树比真实比例小”,都有一条记录。
按工作包推进,每个阶段一道闸门。 工作拆成工程、美术、叙事与界面、测试与复审四条线,按接力顺序推进,每一棒结束写一条接力记录。每个阶段都设一道体验上的闸门,过不了就不进下一阶段:
| 阶段 | 闸门 |
|---|---|
| 0 地基 | 画面对味 |
| 1 活的世界 | 小人是活的 |
| 2 想法与神力 | 出手有手感 |
| 3 时代 | 不干预时能自己走到现代 |
| 4 变体 | 4 种形态都能被达成 |
八、下一步:想法与神力
接下来的阶段 2 要把“涌现”真正交到玩家手里:
- 想法引擎:发现、传播、失传、竞争在画面上的表现(人与人之间飞过的光点、头顶冒出的灵感气泡);
- 7 种常规神力的完整手感:选择、范围预览、施放特效、人群反应(抬头、下跪、逃跑、欢呼)都要在 0.5 秒内给出反馈;
- 第一把火:从一道雷到营地亮起火光的完整演出;
- 导演镜头:大事发生时,镜头会自动压低、推近、环绕,把涌现出来的历史“拍”出来;
- 编年史:一部会自己写的史书,并且记得每件事的起因。
下一篇日志见。如果你对“一个文明会长成什么样”有自己的想象,欢迎留言告诉我们。













