15 KiB
42 · Debug / 开发者工具系统
命名空间
BaseGames.Debug(仅在UNITY_EDITOR || DEVELOPMENT_BUILD下编译)
所属文档集 ← 返回索引 · 总览
依赖BaseGames.Player·BaseGames.Core(GameManager · SaveManager)·BaseGames.Progression
关联 09_EditorExtensions(调试叠加层 §4)· 27_PerformanceBudgetGuide(性能监控)· 31_SaveDataSchema(SaveData 强制写入)
目录
- 系统总览
- 编译条件保护
- DebugConsole — 运行时控制台
- 内置调试命令表
- DebugConfigSO — 开发者配置资产
- 快捷键绑定
- 无敌模式(GodMode)
- 瞬移到房间(Teleport)
- 强制解锁能力(UnlockAbility)
- 强制设置存档标志(SetFlag)
- 性能叠加层(PerfOverlay)
- QA 场景直接启动
- 编辑器菜单集成
1. 系统总览
Debug 系统在开发版 Build 和 Editor 中可用,在 Release Build 中完全剥离,不影响性能和安全性。
Debug 系统职责:
├─ DebugConsole → 运行时命令控制台(类似 Quake/CS 控制台)
├─ DebugConfigSO → 开发者专用配置(无敌、起始场景、默认能力等)
├─ DebugOverlay → 屏幕角实时 FPS/GC/状态显示
├─ CheatCommands → 命令实现集合(god / tp / unlock / setflag 等)
└─ QABootstrapper → 支持任意场景直接播放时正确初始化 Persistent 层
2. 编译条件保护
所有 Debug 代码用 #if 包裹,确保 Release 版本零开销:
// Assets/Scripts/Debug/DebugConsole.cs
#if UNITY_EDITOR || DEVELOPMENT_BUILD
namespace BaseGames.Debug
{
public class DebugConsole : MonoBehaviour
{
// 实现见下方
}
}
#endif
Player Settings 约定
| Build Type | DEVELOPMENT_BUILD |
Debug 功能 |
|---|---|---|
| Editor Play Mode | 自动为 true | ✅ 全功能 |
| Development Build | 勾选 Development Build | ✅ 全功能 |
| Release Build(QA 内部) | 不勾选 | ❌ 完全剥离 |
| Release Build(发行版) | 不勾选 | ❌ 完全剥离 |
3. DebugConsole — 运行时控制台
激活方式
按 ` (反引号 / Tilde) 键呼出/收起控制台覆盖层(UI Toolkit 实现,覆盖在 HUD 上方)。
控制台 UI 结构
┌───────────────────────────────────────────────────────────┐
│ [DEBUG CONSOLE] [×]关闭 │
│ > god on ← 命令输入框 │
│ ───────────────────────────────────────────────────── │
│ [OK] GodMode 已开启 ← 输出日志区 │
│ [OK] 传送至 Room_Cave_03 ← 最新在上 │
│ [ERR] 未知命令: foo ← 错误用红色显示 │
└───────────────────────────────────────────────────────────┘
命令解析
public class DebugConsole : MonoBehaviour
{
// 命令注册表:命令名 → handler
private Dictionary<string, Action<string[]>> _commands = new();
void Awake()
{
// 内置命令自动注册
Register("god", CheatCommands.GodMode);
Register("tp", CheatCommands.Teleport);
Register("unlock", CheatCommands.UnlockAbility);
Register("setflag", CheatCommands.SetFlag);
Register("givegeo", CheatCommands.GiveGeo);
Register("killall", CheatCommands.KillAll);
Register("timescale",CheatCommands.SetTimeScale);
Register("perf", CheatCommands.TogglePerfOverlay);
Register("reload", CheatCommands.ReloadScene);
Register("help", CheatCommands.Help);
}
public void Register(string name, Action<string[]> handler)
=> _commands[name.ToLower()] = handler;
void Execute(string input)
{
var parts = input.Trim().Split(' ');
if (parts.Length == 0) return;
var cmd = parts[0].ToLower();
var args = parts[1..];
if (_commands.TryGetValue(cmd, out var handler))
handler(args);
else
LogError($"未知命令: {cmd}(输入 help 查看命令列表)");
}
}
4. 内置调试命令表
| 命令 | 语法 | 说明 |
|---|---|---|
god |
god [on|off] |
切换无敌模式(无参数 = toggle) |
tp |
tp <SceneName> [SpawnId] |
瞬移到指定场景和出生点 |
unlock |
unlock <AbilityName> |
解锁指定能力(如 unlock doublejump) |
unlock all |
unlock all |
解锁全部能力 |
setflag |
setflag <flagKey> <true|false> |
强制写入 SaveData 任意布尔标志 |
givegeo |
givegeo <amount> |
给予指定数量货币 |
givehp |
givehp <amount> |
设置当前 HP |
killall |
killall |
击杀当前房间所有敌人 |
spawnenemy |
spawnenemy <EnemyId> [x y] |
在指定位置生成敌人 |
timescale |
timescale <0.0–2.0> |
设置 Time.timeScale |
perf |
perf [on|off] |
切换性能叠加层显示 |
reload |
reload |
重新加载当前场景 |
savestate |
savestate |
立即写入存档 |
loadstate |
loadstate |
重新读取最新存档 |
form |
form <FormId> |
强制切换玩家形态(如 form EarthSoul) |
help |
help [cmd] |
显示命令列表或指定命令帮助 |
5. DebugConfigSO — 开发者配置资产
#if UNITY_EDITOR || DEVELOPMENT_BUILD
[CreateAssetMenu(menuName = "Debug/DebugConfig")]
public class DebugConfigSO : ScriptableObject
{
[Header("启动配置")]
public string startSceneOverride; // 非空时 QABootstrapper 加载此场景
public string startSpawnPointId; // 配合 startSceneOverride
[Header("开局默认能力(跳过前期解锁,快速测试后期内容)")]
public bool startWithDoubleJump;
public bool startWithWallGrab;
public bool startWithDash;
public bool startWithSwim;
public string startFormId = "Form_HeavenSoul";
[Header("开局资源")]
public int startGeo = 0;
public int startMaxHP = 5;
[Header("自动化测试")]
public bool enableGodModeAtStart;
public bool skipCutscenes; // 所有 Timeline 直接跳过
public bool skipDialogue; // 所有对话直接结束
}
#endif
DebugConfigSO 资产放在 Assets/Debug/(不加入 Addressable Group),不进入发行包。
6. 快捷键绑定
快捷键仅在 UNITY_EDITOR || DEVELOPMENT_BUILD 下注册,不占用玩家输入:
| 快捷键 | 功能 |
|---|---|
` (Tilde) |
呼出/收起控制台 |
| F1 | 切换无敌模式 |
| F2 | 切换 PerfOverlay |
| F3 | 快速存档 |
| F4 | 快速读档(回到最后存档点) |
| F5 | 重新加载当前场景 |
| F8 | 击杀当前房间所有敌人 |
| Ctrl + Shift + T | 打开 Teleport 快捷面板 |
7. 无敌模式(GodMode)
public static class CheatCommands
{
public static void GodMode(string[] args)
{
bool enable = args.Length == 0
? !PlayerStats.Instance.IsGodMode
: args[0] == "on";
PlayerStats.Instance.IsGodMode = enable;
DebugConsole.Log($"GodMode {(enable ? "开启" : "关闭")}");
}
}
PlayerStats.IsGodMode 属性在 HurtBox.ReceiveDamage 入口处检测:
// HurtBox.cs
#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (_owner.TryGetComponent<PlayerStats>(out var ps) && ps.IsGodMode)
return; // 跳过伤害处理
#endif
8. 瞬移到房间(Teleport)
public static async void Teleport(string[] args)
{
if (args.Length == 0) { DebugConsole.LogError("用法: tp <SceneName> [SpawnId]"); return; }
string sceneName = args[0];
string spawnId = args.Length > 1 ? args[1] : "Default";
// 复用正常的场景加载流程,保证 GameManager / SaveManager 状态一致
var channel = Resources.Load<LoadSceneEventChannelSO>("Events/LoadSceneChannel");
channel.Raise(new LoadSceneEvent(sceneName, spawnId));
DebugConsole.Log($"传送至 {sceneName} @ {spawnId}");
}
注意:Teleport 不影响 SaveData,不触发正常 OnRoomEntered 流程,仅用于测试。
Teleport 快捷面板
Ctrl + Shift + T 打开一个 EditorWindow 风格的浮层(Runtime UI Toolkit),列出所有已知场景名(从 ProgressionSystem.RegionDefinitionSO 读取),点击即瞬移。
9. 强制解锁能力(UnlockAbility)
public static void UnlockAbility(string[] args)
{
if (args.Length == 0) { DebugConsole.LogError("用法: unlock <ability|all>"); return; }
if (args[0] == "all")
{
foreach (AbilityType a in Enum.GetValues(typeof(AbilityType)))
PlayerStats.Instance.UnlockAbility(a);
DebugConsole.Log("已解锁全部能力");
return;
}
if (Enum.TryParse<AbilityType>(args[0], true, out var ability))
{
PlayerStats.Instance.UnlockAbility(ability);
DebugConsole.Log($"已解锁: {ability}");
}
else
{
DebugConsole.LogError($"未知能力: {args[0]}");
DebugConsole.Log("可用值: " + string.Join(", ", Enum.GetNames(typeof(AbilityType))));
}
}
10. 强制设置存档标志(SetFlag)
用于测试"Boss 已击败 → 大门已开"等状态敏感逻辑:
public static void SetFlag(string[] args)
{
// 语法: setflag world.defeatedBossIds Boss_Forest true
if (args.Length < 2) { DebugConsole.LogError("用法: setflag <path> <value>"); return; }
// 通过反射或 switch 路由到 SaveData 对应字段
SaveManager.Instance.SetDebugFlag(args[0], args[1]);
DebugConsole.Log($"标志 [{args[0]}] 已设为 {args[1]}");
}
SaveManager.SetDebugFlag 内部通过 switch 路由已知键路径(不做通用反射,避免安全风险):
public void SetDebugFlag(string path, string value)
{
switch (path)
{
case "world.defeatedBossIds.add":
Current.World.DefeatedBossIds.Add(value); break;
case "player.abilities.swim":
Current.Player.Abilities.Swim = bool.Parse(value); break;
case "player.abilities.dash":
Current.Player.Abilities.Dash = bool.Parse(value); break;
// ... 其他已知路径
default:
UnityEngine.Debug.LogWarning($"[Debug] 不支持的标志路径: {path}"); break;
}
}
11. 性能叠加层(PerfOverlay)
PerfOverlay 在屏幕右上角显示实时诊断数据:
┌──────────────────────────┐
│ FPS: 62 (16.1ms) │
│ GC: 0.2 KB/frame │
│ Draw Calls: 128 │
│ Particles: 34 / 500 │
│ ─────────────────────── │
│ PlayerState: WallGrab │
│ HP: 4/5 Soul: 66/99 │
│ Form: HeavenSoul │
│ Pos: (-12.5, 3.0) │
└──────────────────────────┘
实现基于 Unity FrameTimingManager + GarbageCollector.GetTotalMemory(false),性能采样间隔 0.25 秒,不产生额外 GC。
参见 09_EditorExtensions §4(调试叠加层)获取完整实现细节。
12. QA 场景直接启动
Unity Editor 中直接播放非 Persistent 场景时(如 Room_Cave_03),QABootstrapper 自动初始化必要系统:
#if UNITY_EDITOR || DEVELOPMENT_BUILD
/// <summary>
/// 当非 Persistent 场景被直接播放时,自动加载 Persistent 场景并初始化调试配置。
/// 挂在每个 Room_* 场景的 [Debug] 根物件下(Editor Only)。
/// </summary>
public class QABootstrapper : MonoBehaviour
{
[SerializeField] DebugConfigSO _config;
async void Awake()
{
// 检查 Persistent 场景是否已加载
if (!SceneManager.GetSceneByName("Persistent").isLoaded)
{
await Addressables.LoadSceneAsync(
AddressKeys.ScenePersistent, LoadSceneMode.Additive).Task;
}
// 应用 DebugConfig 起始状态
if (_config != null)
ApplyDebugConfig(_config);
}
void ApplyDebugConfig(DebugConfigSO cfg)
{
var stats = PlayerStats.Instance;
if (cfg.startWithDoubleJump) stats.UnlockAbility(AbilityType.DoubleJump);
if (cfg.startWithWallGrab) stats.UnlockAbility(AbilityType.WallGrab);
if (cfg.startWithDash) stats.UnlockAbility(AbilityType.Dash);
if (cfg.startWithSwim) stats.UnlockAbility(AbilityType.Swim);
if (cfg.enableGodModeAtStart) stats.IsGodMode = true;
var form = FormController.Instance;
form.SwitchForm(cfg.startFormId);
}
}
#endif
13. 编辑器菜单集成
菜单: BaseGames/Debug/
├── [开启 GodMode] → PlayerPrefs 写入临时标志,下次 Play 自动开启
├── [快速跳转场景...] → 打开 SceneSelector EditorWindow
├── [解锁所有能力] → 修改 DebugConfigSO 默认值
├── [清空存档(Slot 0)] → 删除 slot_0.json(确认弹窗)
├── [打开 SaveData 编辑器] → 打开 SaveDataEditor 窗口(见 31_SaveDataSchema §10)
└── [生成 QABootstrapper 到当前场景] → 自动在当前场景添加 QABootstrapper 组件
附录:常见 QA 测试流程
| 测试目标 | 推荐命令序列 |
|---|---|
| 测试 Boss 关卡(跳过前期) | unlock all → tp Boss_Forest |
| 测试后期区域(深渊) | unlock all → tp Room_Abyss_01 |
| 复现特定死亡存档状态 | setflag world.defeatedBossIds.add Boss_Forest → savestate → 手动触发死亡 |
| 性能 Profile 特效密集场景 | perf on → spawnenemy EnemySwarmType 0 0 × 20 |
| 测试游泳系统(无能力) | tp Room_Abyss_Lake_01 → 进入水体观察溺水计时 |
| 测试游泳系统(有能力) | unlock swim → tp Room_Abyss_Lake_01 |
文档版本 1.0 · 2025