Files
zeling_v2/Docs/Design/42_DebugCheatSystem.md
2026-05-08 11:04:00 +08:00

15 KiB
Raw Permalink Blame History

42 · Debug / 开发者工具系统

命名空间 BaseGames.Debug(仅在 UNITY_EDITOR || DEVELOPMENT_BUILD 下编译)
所属文档集 ← 返回索引 · 总览
依赖 BaseGames.Player · BaseGames.CoreGameManager · SaveManager· BaseGames.Progression
关联 09_EditorExtensions调试叠加层 §4· 27_PerformanceBudgetGuide性能监控· 31_SaveDataSchemaSaveData 强制写入)


目录

  1. 系统总览
  2. 编译条件保护
  3. DebugConsole — 运行时控制台
  4. 内置调试命令表
  5. DebugConfigSO — 开发者配置资产
  6. 快捷键绑定
  7. 无敌模式GodMode
  8. 瞬移到房间Teleport
  9. 强制解锁能力UnlockAbility
  10. 强制设置存档标志SetFlag
  11. 性能叠加层PerfOverlay
  12. QA 场景直接启动
  13. 编辑器菜单集成

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 BuildQA 内部) 不勾选 完全剥离
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.02.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_03QABootstrapper 自动初始化必要系统:

#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 alltp Boss_Forest
测试后期区域(深渊) unlock alltp Room_Abyss_01
复现特定死亡存档状态 setflag world.defeatedBossIds.add Boss_Forestsavestate → 手动触发死亡
性能 Profile 特效密集场景 perf onspawnenemy EnemySwarmType 0 0 × 20
测试游泳系统(无能力) tp Room_Abyss_Lake_01 → 进入水体观察溺水计时
测试游泳系统(有能力) unlock swimtp Room_Abyss_Lake_01

文档版本 1.0 · 2025