下载 App
开发日志 02|48 关共用一个世界:把关卡写成数据
《一念万物》有 48 关,分为春夏秋冬,每关还有后续段。如果给每关单独写一套逻辑,类似的水、风、鸟和植物规则很快就会分叉:某一关改好了,另一关仍然沿用旧行为。项目因此把世界演化集中在 World.gd,关卡只描述初始条件和目标。

图 1|依据代码绘制的结构示意。关卡、终章与自在园使用同一个引擎,各自通过数据定义条件与目标。
先确定规则与数据的边界
Data/levels.json 保存关卡对象、位置、天气、风、时间线以及可接受的和谐方案。石头能不能拖、植物需要怎样的光照、鸟喜欢什么落脚点,都由对象属性表达。World.gd 负责解释这些属性,推进对象状态。
例如,第一关通过石头阻水改变池塘状态;第二关通过移动遮挡物改变植物受到的光。两关的矛盾不同,但它们没有各自的脚本类。新增关卡时,可以组合已经存在的关系;需要新能力时,再把能力加入世界引擎,并让其他关卡也能使用。
这种边界并不意味着所有设计都能只改 JSON。新的关系仍然需要实现与验证,只是实现后成为公共能力,而不是藏在某一关里。
用局部环境连接对象
世界每步依次推进硬物理、生物需求、表现和稳定检测。对象查询所在位置的风、光、温度与湿度,而不是只读取一个全局天气标签。
屏风背后可以避风,云下光照较低,火会改变附近的温度,池塘水位会影响湿地范围。同样是下雨,屋檐下与雨云外的植物可能没有直接被淋到。这使局部布局真正参与玩法。
第 13 关“荷上雨”的调整体现了这件事。当前数据不再放房屋,局部雨云只淋池上荷叶,雨外的花依靠荷叶送来的水。第二段还要引走多余的水。删掉房屋以后,雨、荷叶和花的关系更集中;局部降雨与荷叶传水仍沿用公共规则。

图 2|2026-10-12 第 13 关开场实机截图。当前布局没有房屋,雨云覆盖荷叶,右侧花盆在雨外;雨水传递使用公共规则。
后续段继续使用前一段的世界
48 关都有第二段,其中九关还有第三段。进入下一段时不会重新加载一幅独立场景,而是在前一段的状态上继续演化。
段数据中的 add 可以加入新访客,do 立即改变天气或其他状态,timeline 安排从这一段开始计时的事件,sols 则指定这一段接受的结果。干预次数按段重新计数,发现、念痕和因果记录可以跨段积累。
这里还需要处理恢复逻辑。每段开始时,世界会保存一份状态,“回到清晨”先回到当前段的起点。对于云从画外飘入等开场过程,数据可以通过 arrive 指定开场结束后更新恢复点,避免每次重试都重新等待同一段演出。
自在园检验规则能否脱离关卡成立
自在园使用 Data/sandbox.json,仍然由同一个世界引擎运行。没有 sols 就不进行通关判定;底部架子上的 kit 描述可拿入与放回的物件。
石头放进溪里应该阻水,取走后应该恢复,灯移动后仍然应该影响附近的光。这些行为不依赖“正在玩第几关”。自在园需要的能力也必须加进通用引擎。
它也暴露了布局必须与规则对应的问题。当前溪流矩形是 [332, 520, 56, 300],下游伸进池塘。自检分别检查满水和最低水位时溪口仍在池内,避免画面看起来接通、实际计算却断开的情况。

图 3|2026-10-12 自在园实机截图。底部架子提供可拿入世界的物件,溪流与池塘仍由相同的水流规则运行。
集中规则之后,也要集中承担回归成本
所有世界共用规则,一个水流改动可能影响很多关。因此,关卡数据同时保留已知操作路线、替代路线和不操作时的预期结果,自检逐段核对。
当前 JSON 仍是直接维护的运行时数据,没有同时维护另一份策划表。以后如果引入表格,需要确定唯一源表和编译方式,否则两份内容很容易不一致。
这套结构让每关的差异落在关系与布局上,也让开发时的责任更明确:数据描述一个世界,公共引擎负责让它按同样的规则运行。











