摘要:本文聚焦「关卡生成算法(PCG)」,梳理核心概念、关键方法与落地实践。
📚 1. 理论基础 (Theoretical Basis)
🎯 核心定义
程序化内容生成 (Procedural Content Generation, PCG) 是通过算法自动创建游戏内容的技术。对于 Roguelike 游戏,PCG 是核心支柱。 PCG 的优势:- 无限内容 - 避免重复感
- 降低成本 - 减少手工设计工作量
- 增加寿命 - 每次游玩都不同
- 质量控制 - 生成结果可能不可玩
- 性能开销 - 生成算法可能很慢
- 平衡性 - 难度/奖励可能失衡
📐 核心算法分类
1. WFC (Wave Function Collapse) - 波函数坍缩
原理: 基于约束传播的瓦片拼接算法。- ✅ 生成结果始终符合规则
- ✅ 适合复杂约束
- ✅ 可以生成有机感的关卡
- ❌ 可能陷入无解状态(需要回溯)
- ❌ 性能较慢
- ❌ 规则定义复杂
2. BSP (Binary Space Partitioning) - 二叉空间分割
原理: 递归分割空间,创建房间和走廊。- ✅ 简单易实现
- ✅ 生成速度快
- ✅ 房间分布均匀
- ❌ 生成结果较规整(缺乏有机感)
- ❌ 走廊可能冗长
- ❌ 不够随机
3. Cellular Automata - 元胞自动机
原理: 基于简单规则的迭代演化。- ✅ 生成自然的洞穴/有机形状
- ✅ 实现简单
- ✅ 参数易调
- ❌ 不保证连通性
- ❌ 难以控制具体形状
- ❌ 需要后处理(连通性检测)
🛠️ 2. 实践应用 (Practical Implementation)
🎮 Vampirefall 地图生成框架
混合生成策略
Vampirefall 的塔防+肉鸽特性需要手工设计 + 程序生成混合:🗂️ 数据结构
MapTemplate.cs
ProceduralMapGenerator.cs
🎨 路径保证算法
问题: 程序生成可能产生不可通行的路径。 解决方案: 路径优先生成 + 验证 + 修复。🌟 3. 业界优秀案例 (Industry Best Practices)
🎮 案例 1: Spelunky - 模板拼接大师
核心机制
Spelunky 使用预制房间模板 + 智能拼接生成关卡。 生成流程:- 预制塔防模板(不同地形)
- 模板库分类(简单/困难/Boss)
- 主路径保证算法
🎮 案例 2: The Binding of Isaac - 房间库系统
核心机制
Isaac 使用大量手工设计房间 + 随机组合。 房间库规模:“程序生成不是为了减少工作量,而是为了增加重玩价值。”Vampirefall 借鉴:
- 建立塔防场景库(100+)
- 基于难度分级
- 避免同一局重复
🎮 案例 3: Enter the Gungeon - 程序化地牢
核心机制
Gungeon 结合了BSP 分割 + 手工房间 + 特殊规则。 生成算法:- BSP 用于大区域划分
- 关键房间(Boss/商店)位置规则
- 秘密区域设计
🔗 4. 参考资料 (References)
📄 理论
-
Procedural Content Generation in Games
作者: Noor Shaker, Julian Togelius, Mark J. Nelson
书籍链接 -
Wave Function Collapse Algorithm
Maxim Gumin
GitHub
📺 GDC
-
[GDC 2017] Spelunky Level Generation
演讲者: Derek Yu
YouTube -
[GDC 2015] Diablo’s Dungeon Generation
演讲者: Mike Barlow (Blizzard)
GDC Vault
🌐 博客
-
The Binding of Isaac Room Design
Edmund McMillen Blog -
Procedural Map Generation Techniques
RogueBasin Wiki
🎯 附录:Vampirefall PCG 实施检查清单
✅ 阶段 1: 模板系统(必须)
- 创建 10+塔防地图模板
- 定义路径节点和塔位
- 建立模板库管理器
✅ 阶段 2: 生成算法(必须)
- 实现 BSP 或房间拼接
- 路径保证算法
- 连通性验证
✅ 阶段 3: 随机性(推荐)
- 障碍物随机放置
- 资源点分布
- 敌人刷新点变化
✅ 阶段 4: 验证系统(必须)
- 可玩性检测
- 难度评估
- 种子记录(用于 bug 复现)
✅ 阶段 5: 调试工具(推荐)
- 可视化生成过程
- 种子输入功能
- 性能监控
最后更新: 2025-12-04
维护者: Vampirefall 设计团队
波函数坍缩 (WFC) 生成
[!NOTE] 核心思想: WFC 是一种基于约束 (Constraint) 的生成算法。它不是告诉计算机“怎么画”,而是告诉它“什么不能画”。在 Roguelike 地牢生成中,WFC 能比传统的“房间+走廊”算法生成更自然、更有机的结构。
1. 核心概念 (Core Concepts)
1.1 叠加态 (Superposition)
在算法开始时,地图上的每一个格子都同时处于“所有可能状态”的叠加中。- 例如:一个格子既可能是“草地”,也可能是“墙壁”,也可能是“水”。
1.2 熵 (Entropy)
熵是衡量不确定性的指标。- 高熵: 该格子有很多种可能性 (例如:草地/墙/水/路)。
- 低熵: 该格子只有很少的可能性 (例如:只能是墙)。
- 熵为0: 该格子状态已确定 (坍缩)。
1.3 坍缩 (Collapse)
观测导致坍缩。我们人为地(或随机地)选择一个格子,将其状态固定下来。1.4 约束传播 (Constraint Propagation)
一旦一个格子确定了(例如变成了“水”),它的邻居就不可能是“岩浆”(假设水火不容)。这种约束会像波纹一样向四周传播,减少邻居的熵。2. 算法流程 (Algorithm Flow)
- 初始化: 将网格中所有单元格设为叠加态 (包含所有可能的 Tile)。
-
寻找最小熵: 找到当前网格中熵最小(可能性最少)但尚未坍缩的单元格。
- 如果有多个最小熵,随机选一个。
- 观测/坍缩: 根据权重随机选择该单元格的一个状态,将其固定。
-
传播:
- 更新该单元格的所有邻居。
- 如果邻居的可能性减少了,继续更新邻居的邻居 (递归或堆栈)。
- 如果在传播过程中某个单元格的可能性变为 0 (无解),则生成失败,需要回溯或重试。
- 循环: 重复步骤 2-4,直到所有单元格都坍缩完成。
3. 实现指南 (Implementation Guide)
3.1 定义 Tile 与 Socket (接口)
为了判断两个 Tile 是否能相邻,我们需要定义它们的边缘接口 (Socket)。 假设我们有 4 个方向:上、下、左、右。- 草地 Tile:
[Grass, Grass, Grass, Grass] - 海岸 Tile:
[Water, Grass, Coast, Coast](假设)
3.2 旋转与镜像
为了节省美术资源,我们可以自动生成旋转变体。- 一个不对称的 Tile 可以生成 4 个旋转版本。
- 接口数据也需要随之旋转。
3.3 回溯与重试 (Backtracking)
WFC 可能会遇到“死胡同” (Contradiction)。- 简单策略: 遇到冲突直接清空整个地图重来 (对于小地图非常快)。
- 高级策略: 记录每一步的状态,遇到冲突回退到上一步,并标记导致冲突的分支为不可行。
3.4 性能优化
- 最小堆 (Min-Heap): 用最小堆维护所有格子的熵,以便 O(1) 取出最小熵格子。
- 脏标记 (Dirty Flags): 传播时只处理受影响的区域。
4. Unity 代码片段 (伪代码)
5. 业界案例 (Industry Cases)
Townscaper
- 特点: 极其流畅的实时 WFC。
- 机制: 玩家点击只是“观测”了一个格子,算法自动计算周围格子的最优解(屋顶、地基、楼梯)。
Bad North (北方绝境)
- 特点: 微型岛屿生成。
- 应用: 保证岛屿边缘总是平滑的海岸线,且高低差有梯子连接。
Caves of Qud
- 特点: 极其复杂的文本描述生成。
- 应用: 利用 WFC 生成历史传说和地貌描述。