🧙♂️ 肉鸽组合快速测试系统
📚 1. 理论基础 (Theoretical Basis)
1.1 核心定义:组合爆炸与确定性验证
在 Roguelike (+ Tower Defense + Looter) 游戏中,核心乐趣来源于**“构建 (Build)”**。然而,随着词条(Affixes)、天赋(Perks)、装备(Items)的增加,可能的组合数量呈指数级增长。- 组合爆炸 (Combinatorial Explosion): 假设有 100 种天赋,玩家能选 10 种,组合数是天文数字。手动测试每一种组合的化学反应是不现实的。
- 确定性验证 (Deterministic Verification): 开发者的目标不是测试所有组合,而是快速构建特定场景,验证机制的预期行为(例如:“当玩家拥有[多重投射]且装备[爆炸火花]时,是否会引发性能卡顿?”)。
1.2 为什么需要“快速”? (Why Speed Matters)
- 心流阻断: 如果策划想要测试一个新词条,需要跑图 10 分钟才能随机刷到,测试效率极低。
- 边缘情况 (Edge Cases): 很多 Bug 只有在极端数值或特定罕见组合下才会触发。
- 回归测试: 每次修改伤害公式,都需要快速验证旧的 Build 是否崩坏。
🛠️ 2. 实践应用 (Practical Implementation)
2.1 快速构建系统 (Instant Build System)
在 Vampirefall 中,我们通过以下三层架构实现快速构建:A. 调试控制台 (Debug Console)
最基础的层级,通过命令直接修改当前状态。B. Build 代码化 (Build Strings)
借鉴卡牌游戏的卡组代码,将一个完整的 Build 序列化为字符串。- 用途: 策划 A 发现一个强力组合,复制字符串发给程序 B,B 粘贴即可瞬间复现该状态。
- 数据结构:
压缩后:
VF1.0|VD01|L20|P:MS3,CL1,BP1|I:SL01,AE02
C. 预设存档槽 (Preset Save Slots)
在开发版本的主菜单提供 “Load Preset” 功能,预设多种典型环境:- Glass Cannon: 极高攻,极低防(测试秒杀逻辑)。
- Tank: 极高防,高回血(测试受击回能、反伤)。
- Proc Machine: 高频触发(测试特效性能、显卡压力)。
2.2 终极木头人系统 (The Ultimate Target Dummy)
木头人不能只是一个血量无限的沙袋,它必须是可配置的测试环境。A. 木头人配置面板 (Dummy Configuration)
在训练场(Hub World)中,玩家/开发者可以与控制台交互,设定木头人的属性:- 防御属性 (Defense):
0 甲vs高甲(测试穿透/破甲)。0 抗vs火抗 75%(测试元素穿透)。
- 行为逻辑 (Behavior):
- Idle: 纯挨打,测试 DPS。
- Attack (0 Damage): 以固定频率攻击玩家,但造成 0 伤害。关键功能,用于测试 “闪避后触发”、“受击触发”、“格挡触发” 的词条。
- Move: 移动靶,测试弹道追踪性能。
- 数量 (Crowd):
- 生成 1 个 vs 生成 50 个 (测试 AoE 效率和性能)。
B. 实时 DPS 统计 (Real-time DPS Meter)
在木头人上方或屏幕侧边显示详细战斗数据:Combat Log
- DPS: 15,400 (Peak: 22,000)
- Last Hit: 1,200 (Critical!)
- Damage Breakdown:
- Physical: 40%
- Fire: 30% (Ignite: 10%)
- Proc (Lightning): 20%
C. 快速重置 (Instant Reset)
一键清除所有 DoT (Damage over Time) 状态,回满血,重置 DPS 统计。由于 DoT 伤害计算复杂,测试爆发伤害时必须能快速清空环境。2.3 快速修改配置 (Hot-Reload Configs)
利用 Luban 和 Odin Inspector 实现运行时配置热重载。- 流程:
- 策划在 Excel/JSON 中修改装备数值。
-
运行
gen_data.bat。 - Unity 监听到文件变动,自动重载内存中的表格数据(无需重启游戏)。
- 通过 EventBus 通知 UI 和 实体 刷新数值。
🌟 3. 业界优秀案例 (Industry Best Practices)
3.1 Hades (黑帝斯)
- Skelly (骷髅沙袋):
- 优点: 永远在那,打死后快速复活,不废话。它是一个“角色”,有性格,通过对话解锁功能,沉浸感极强。
- 借鉴点: 不要把测试工具做得太像调试软件,尽量包装进游戏世界观。
- Mirror of Night (夜之镜):
- 允许玩家随时重洗天赋,方便尝试不同 Build。
3.2 Warframe (星际战甲) - Simulacrum (幻影装置)
- 机制: 玩家可以生成任何已扫描过的敌人。
- 神级功能:
- Pause AI: 让敌人不动,方便爆头测试。
- Invincibility: 玩家无敌,测试玻璃大炮 Build 而不死。
- Enemy Level: 只有在这里能测试 9999 级敌人的护甲减伤曲线。
- 借鉴点: 对于长线运营游戏(Looter),必须提供一个能模拟后期高难环境的场所。
3.3 The Binding of Isaac (以撒的结合)
- Modding Console:
- 极其强大的控制台,
/g item_name直接获得物品。 - 社区依托此功能挖掘了大量深层机制。
- 极其强大的控制台,
- 缺点: 需要开启 Debug 模式,且开启后无法获得成就(防作弊)。
🧩 4. 深度技术实现 (Deep Tech Implementation)
4.1 Build Code 序列化算法 (Build Code Serialization)
要实现类似《炉石传说》或《流放之路》的卡组/Build 代码,核心是将复杂对象映射为紧凑字符串。A. 算法步骤
-
映射 (Mapping): 建立一个全局静态字典,将长 ID 映射为短整型或 Base64 字符。
"Perk_Thunder_Strike_Level_Max"(30 chars) ->1042(Integer) ->A2(Base64)
-
分割 (Delimiting): 使用特定字符分割不同模块。
|分割大模块 (英雄、天赋、装备)。;分割列表项。:分割键值对 (ID:等级)。
-
压缩 (Compression):
- Varint: 对于数字存储,使用 Varint 编码(小数字占用更少字节)。
- RLE (Run-Length Encoding): 虽不常用,但如果有重复物品
[A, A, A, A, B]->4A1B。
- 校验 (Checksum): 在末尾添加 CRC32 或简单的 Hash,防止玩家输错代码导致崩端。
B. C# 代码参考
4.2 全状态快照系统 (Snapshot Dump & Restore)
用户报 Bug 时一键 Dump 并在编辑器还原,是顶级开发体验。这个系统的开发难度取决于你的还原精度。📊 难度分级 (Complexity Tiers)
| 级别 | 还原精度 | 适用场景 | 开发难度 | 描述 |
|---|---|---|---|---|
| L1 | 静态数据 (Static) | 配装/面板 Bug | ⭐ (简单) | 仅保存玩家身上的装备、天赋、属性。本质上就是“存档”。 |
| L2 | 战斗开局 (Session) | 进场报错/初始掉落 | ⭐⭐ (中等) | 保存 L1 + 关卡 Seed + 当前波次索引。能还原出一模一样的怪和地图,但不还原你刚才打了一半的战况。 |
| L3 | 完美现场 (Exact State) | AI 寻路/物理卡死 | ⭐⭐⭐⭐⭐ (地狱) | 需要还原所有怪物的坐标、血量、Buff、飞在空中的子弹位置。除非是 ECS 架构或确定性帧同步,否则极难实现。 |
🛠️ L2 级别的实现方案 (推荐)
对于大多数 Roguelike,还原到**“进入该房间的那一刻”**通常就足够复现 Bug 了。 1. 数据结构 (DumpData)-
右键
bug_report.dump-> “Play in Editor”。 -
Unity 启动,进入
Bootstrap场景。 -
Bootstrap检测到启动参数或静态变量中持有 Dump 数据。 -
跳过主菜单,直接初始化
GameManager。 -
MapGenerator使用 Dump 中的rng_seed重新生成地图(保证地形一致)。 -
EnemyManager模拟快进到第 5 波(或者直接设置波次索引)。
💡 开发建议
- 不要过度追求 L3: 还原每一颗子弹的位置通常是不划算的。只要能还原出 Player Build + Map Seed + Enemy Wave,95% 的数值和机制 Bug 都能复现。
- JSON 甚至可以加密: 让玩家在 Discord 上发给你一串加密乱码,你丢进编辑器就能跑,非常酷且能保护存档结构不被轻易修改。
4.3 L2 级别快照系统实战指南 (L2 Snapshot Implementation Guide)
L2 级别的目标是**“只要提供由于同一个 Seed 生成的关卡,以及完全一致的玩家属性,就能复现 99% 的战斗逻辑问题”**。A. 核心数据结构 (The Data Model)
我们需要定义一个能完全描述“战前状态”的类。B. 劫持初始化流程 (Hijacking Initialization)
普通游戏的启动流程漫长且繁琐(Splash -> MainMenu -> LoadSave -> Battle)。我们需要**“短路”**这个过程。 Step 1: 创建调试上下文 (DebugContext) 这是一个既能在 Editor 运行,也能在 Runtime 访问的静态桥梁。GameManager 或 BattleBootstrap)中处理劫持逻辑。
C. 如何获取 Seed? (The Seed Strategy)
要让 L2 方案生效,你的游戏中必须显式管理所有的随机性。- ❌ 错误做法: 到处写
UnityEngine.Random.Range(0, 100)。这样无法通过一个种子还原所有行为。 - ✅ 正确做法: 封装一个
RandomService。
🔗 4. 参考资料 (References)
- GDC: Building the Tools for a Roguelike (Hades)
- Blog: Deterministic Testing in Procedural Games
- Tools: Odin Inspector (用于创建游戏内调试面板)