WFC适用于生成结构化、接口约束明确的地牢(如城堡、模块化建筑),而非连通性优先的迷宫;C++实现需手写tile结构体与方向接口ID,用位掩码+查表优化熵计算,BFS传播防栈溢出,并必须实现回退机制。

WFC 在 C++ 游戏中做地牢生成,不是“能不能”,而是“要不要承担它的开销”。它能产出高度结构化、约束明确的布局(比如房间必须连走廊、门必须对齐墙),但不擅长生成连通性优先的迷宫式地牢——那更适合 BSP 或 Prim 算法。如果你要的是“每扇门都严丝合缝接上另一扇门”的城堡、城市街区或模块化建筑,WFC 才是正解。
WFC 的核心不是图像采样,而是 tile 接口约束定义
很多初学者直接拿 WFC 去处理像素图,结果卡在边缘匹配逻辑里。C++ 实现地牢 WFC,第一步必须跳过图像解析,直接手写 tile 元数据:
-
tile不是图片,而是一个带 4 个方向接口(up/down/left/right)的结构体,每个接口填一个整数 ID(如0表示“可通行走廊”,1表示“实心墙”,2表示“门”) - 约束表本质是二维数组:
compatible[up_id][down_id]→true表示允许上下拼接;同理定义left-right和up-down的兼容关系 - 别用 map
> 存兼容表——查找慢;用 std::array<:array>, 8>(假设最多 8 种接口 ID)更高效 - 常见错误:
doortile 的up和down都设为2,但忘了在兼容表里加compatible[2][2] = true,导致无法垂直堆叠门
熵计算与坍缩必须用 std::vector + 位掩码优化
每个格子初始状态是“所有 tile 都可能”,即一个长度为 N 的布尔向量。但 N 一过百,每次遍历求熵(非零项计数)就成性能瓶颈。实际工程中应:
用 Godot 4 + GDScript 开发 3D 游戏的端到端工作流。触发场景:"做一个 3D 游戏"、 "第一人称/第三人称/FPS/TPS/3D 平台跳跃"、"用 Godot 做 3D"、"加个 3D 角色控制器/ 相机/敌人寻路/关卡"、"导入 glb 模型不显示"、"3D 场景太暗/太卡"、"make a 3D game"、 "first-per
- 把 tile ID 映射为 bit 位:第 0 个 tile → bit 0,第 1 个 → bit 1……用
uint64_t存最多 64 种 tile;超 64 就分两组,避免std::vector的代理引用开销 - 熵值不实时计算,改用预存的 popcount 查表:
static constexpr uint8_t POPCOUNT[256] = { ... };,每次更新 bitset 后查低 8 位、中 8 位、高 8 位即可 - 坍缩时,不是“随机选一个剩余 tile”,而是先找最小熵格子(熵 == 1 的优先),再从它的 bitset 中用
__builtin_ctzll(mask)取最低位——比std::rand()快且确定性更强,方便调试 - 错误现象:用
std::vector存候选 tile ID,每次坍缩都erase,导致大量内存移动;实际应只维护 bitset,坍缩后才批量重建索引
传播阶段最容易栈溢出或死循环
一个格子坍缩后,要通知四邻格更新其可能 tile 集合。这个过程若不做剪枝,会无限递归或反复触发同一格子。关键控制点:
- 传播必须用队列(BFS),不是递归(DFS)——避免深调用栈炸掉,尤其在 128×128 地图上
- 每个格子加访问标记(
propagated[i][j]),入队前检查是否已入队;清空队列后统一重置标记,别在传播中实时改 - 约束检查必须严格:邻居格子当前所有剩余 tile 中,至少有一个能在对应方向匹配本格子的接口 ID;一个都不匹配 → 冲突(contradiction)→ 必须回退
- 没实现回退机制?那就别用 WFC 做地牢生成。最简回退就是保存最近 3 层状态快照(用
std::vector<:vector>>),冲突时 rollback 并换一个坍缩选项;否则程序直接 abort
真正难的从来不是“怎么让波函数塌缩”,而是“怎么定义哪些塌缩结果算‘地牢’”。比如你允许 room tile 四周全是 wall,那生成出来的就是孤岛房间;但加一条约束“每个 room 至少有一个方向接口是 door”,整个逻辑就变复杂——这些语义规则不会自动出现,得你一行行写进兼容表和传播条件里。WFC 是把设计决策编译成约束的工具,不是魔法生成器。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










