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

429 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 42 · Debug / 开发者工具系统
> **命名空间** `BaseGames.Debug`(仅在 `UNITY_EDITOR || DEVELOPMENT_BUILD` 下编译)
> **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md)
> **依赖** `BaseGames.Player` · `BaseGames.Core`GameManager · SaveManager· `BaseGames.Progression`
> **关联** 09_EditorExtensions调试叠加层 §4· 27_PerformanceBudgetGuide性能监控· 31_SaveDataSchemaSaveData 强制写入)
---
## 目录
1. [系统总览](#1-系统总览)
2. [编译条件保护](#2-编译条件保护)
3. [DebugConsole — 运行时控制台](#3-debugconsole--运行时控制台)
4. [内置调试命令表](#4-内置调试命令表)
5. [DebugConfigSO — 开发者配置资产](#5-debugconfigso--开发者配置资产)
6. [快捷键绑定](#6-快捷键绑定)
7. [无敌模式GodMode](#7-无敌模式godmode)
8. [瞬移到房间Teleport](#8-瞬移到房间teleport)
9. [强制解锁能力UnlockAbility](#9-强制解锁能力unlockability)
10. [强制设置存档标志SetFlag](#10-强制设置存档标志setflag)
11. [性能叠加层PerfOverlay](#11-性能叠加层perfoverlay)
12. [QA 场景直接启动](#12-qa-场景直接启动)
13. [编辑器菜单集成](#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 版本零开销:
```csharp
// 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 ← 错误用红色显示 │
└───────────────────────────────────────────────────────────┘
```
### 命令解析
```csharp
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 — 开发者配置资产
```csharp
#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
```csharp
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` 入口处检测:
```csharp
// HurtBox.cs
#if UNITY_EDITOR || DEVELOPMENT_BUILD
if (_owner.TryGetComponent<PlayerStats>(out var ps) && ps.IsGodMode)
return; // 跳过伤害处理
#endif
```
---
## 8. 瞬移到房间Teleport
```csharp
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
```csharp
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 已击败 → 大门已开"等状态敏感逻辑:
```csharp
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` 路由已知键路径(不做通用反射,避免安全风险):
```csharp
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` 自动初始化必要系统:
```csharp
#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*