[!NOTE] 核心思想: WFC 是一种基于约束 (Constraint) 的生成算法。它不是告诉计算机“怎么画”,而是告诉它“什么不能画”。
在 Roguelike 地牢生成中,WFC 能比传统的“房间+走廊”算法生成更自然、更有机的结构。
在算法开始时,地图上的每一个格子都同时处于“所有可能状态”的叠加中。
熵是衡量不确定性的指标。
观测导致坍缩。我们人为地(或随机地)选择一个格子,将其状态固定下来。
一旦一个格子确定了(例如变成了“水”),它的邻居就不可能是“岩浆”(假设水火不容)。这种约束会像波纹一样向四周传播,减少邻居的熵。
为了判断两个 Tile 是否能相邻,我们需要定义它们的边缘接口 (Socket)。
假设我们有 4 个方向:上、下、左、右。
[Grass, Grass, Grass, Grass][Water, Grass, Coast, Coast] (假设)规则: Tile A 的“右”接口必须与 Tile B 的“左”接口匹配 (可以是相同 ID,也可以是定义的对称 ID)。
为了节省美术资源,我们可以自动生成旋转变体。
WFC 可能会遇到“死胡同” (Contradiction)。
public class WFCGenerator : MonoBehaviour {
struct Cell {
public bool Collapsed;
public List<Tile> PossibleTiles;
public int Entropy => PossibleTiles.Count;
}
void RunWFC() {
// 1. Init
InitializeGrid();
while (IsAnyCellUncollapsed()) {
// 2. Find Min Entropy
Vector2Int coords = GetMinEntropyCell();
// 3. Collapse
CollapseCell(coords);
// 4. Propagate
PropagateConstraints(coords);
}
DrawMap();
}
void PropagateConstraints(Vector2Int startNode) {
Stack<Vector2Int> stack = new Stack<Vector2Int>();
stack.Push(startNode);
while (stack.Count > 0) {
var current = stack.Pop();
foreach (var neighbor in GetNeighbors(current)) {
if (Constrain(current, neighbor)) {
stack.Push(neighbor); // 如果邻居被修改了,继续传播
}
}
}
}
}