跳转到内容

17 – Dev 控制台:SusConsoleService, SusConsoleDriver

服务:SusConsoleService (sus-core/Runtime/Services/) 驱动:SusConsoleDriver (sus-core/Runtime/Services/)


1. 用途

一个游戏内 dev 控制台:拦截 Unity 日志,并把它们显示在整个 UI 之上 (分类 Console = 50 — 最顶层),支持过滤/搜索,并可以执行 命令。主要使用场景是在没有 Editor Console 的设备/build 上调试。

SUS dev console overlay showing filter chips, search field, and colored log entries

2. 接入

#if 包裹,把它排除在 release 构建之外:

csharp
#if DEVELOPMENT_BUILD || UNITY_EDITOR
var overlay = SusBootstrap.GetOrCreateOverlay(uiDocument.rootVisualElement);
var console = new SusConsoleService
{
    OverlayHost = overlay,
    ToggleKey = KeyCode.BackQuote, // ~
    MaxEntries = 500,
};
console.Attach(); // subscribes to the log, spawns the hotkey driver, sets Instance
#endif

Attach() 做了四件事:

  1. 订阅 Application.logMessageReceivedThreaded
  2. 创建(或查找)一个 SusConsoleDriver,在 Update 中轮询 ToggleKey
  3. 注册内置命令(clearhelpfilter)。
  4. 把实例发布为 SusConsoleService.Instance(setter 是私有的 — 不要直接给它赋值)。

Detach() 会取消日志订阅并隐藏 UI。


3. 使用

界面

按下 ~ 会从底部打开控制台(占屏幕高度的 40%),是一个深色面板:

元素说明
All / Log / Warn / Err 按钮按类型过滤
搜索框按子串过滤(不区分大小写)
命令输入框输入命令 → Enter → 执行
命令输入框中的 Tab命令名补全
关闭控制台

日志按颜色区分:灰色(Log)、黄色(Warning)、红色(Error/Exception/Assert)。 新消息出现时自动向下滚动;手动向上滚动会停止自动滚动。

样式

控制台没有 inline C# 样式:整个 overlay 由 Runtime/Resources/SusRuntime/sus-console.uss 描述,服务在首次 Show() 时 将其加载到自己的 root 上。颜色来自 design token(--sus-bg-overlay--sus-primary--sus-warning--sus-error 等),因此控制台会跟随当前主题。

要重新设置样式,可以在控制台自身的 sheet 之后加载的任意 sheet 中覆盖这些类 (面板上的项目 sheet,或通过 SusBootstrap.RegisterCascadeStyleSheet):

元素
.sus-consoleOverlay 面板(position、height、background)
.sus-console__toolbar顶部一行
.sus-console__filter / --activeAll / Log / Warn / Err 过滤 chip
.sus-console__field + __search / __command文本输入框
.sus-console__close✕ 按钮
.sus-console__list承载日志行的 ScrollView
.sus-console__line + --warning / --error日志行
.sus-console__status底部状态行(过滤器 + 条目数)

sheet 中的每一个 var() 都带有 fallback 值,因此即使控制台运行在从未经过 SusBootstrap、没有 token 级联的裸面板上,外观依然正确。

注册命令

csharp
#if DEVELOPMENT_BUILD || UNITY_EDITOR
SusConsoleService.Instance.RegisterCommand("spawn", args =>
{
    if (args.Length > 0)
        SpawnUnit(args[0]);
    else
        Debug.Log("Usage: spawn <unitId>");
}, help: "Spawn a unit by id");

SusConsoleService.Instance.RegisterCommand("gold", args =>
{
    int amount = args.Length > 0 ? int.Parse(args[0]) : 1000;
    player.Gold += amount;
    Debug.Log($"Added {amount} gold. Total: {player.Gold}");
}, help: "Add gold (default 1000)");
#endif

内置命令(在 Attach 中自动注册):

命令说明
clear清空日志缓冲区
help列出所有命令
filter <all|log|warn|error>切换过滤器

4. 架构

线程安全

Application.logMessageReceivedThreaded 可能从后台线程调用。 条目会在 lock 保护下追加到 Queue<SusLogEntry>SusConsoleDriver.Update() 在主线程调用 DrainPendingEntries() — 它把记录搬进环形 缓冲区并更新 UI。

环形缓冲区

溢出时(MaxEntries,默认 500),旧条目会被淘汰:

csharp
if (_buffer.Count >= MaxEntries)
    _buffer.RemoveAt(0);
_buffer.Add(entry);

惰性构建 UI

UI(_root_scrollView、toolbar、命令输入框)在首次 Show() 时才构建。 关闭状态下,控制台不会创建任何 VisualElement — 零渲染开销。

Z-order

OverlayCategory.Console = 50screen-space OverlayHost 的最顶层。 控制台始终位于 modal、tooltip、dropdown 和 toast 之上。

World-space UI(血条)属于这个层级栈:它使用一个独立的面板, 位于屏幕之下。参见 07-overlayhost.md

World-space panel                         ← UNDER screens (not OverlayHost)
└── healthbar / nameplate / …

Screen UIDocument
├── SusRouteView                          ← screens
└── OverlayHost
     ├── [Transition = 10]
     ├── [Modal      = 20]
     ├── [Tooltip    = 30]  ← above modals
     ├── [Dropdown   = 40]
     ├── [Toast      = 45]
     └── [Console    = 50]  ← console here (topmost)

5. API

SusConsoleService

方法/属性类型说明
Attach()void日志订阅 + hotkey 驱动
Detach()void取消订阅 + 关闭
Show()void显示控制台
Hide()void隐藏控制台
Toggle()void切换显示/隐藏
Clear()void清空缓冲区
DrainPendingEntries()void把记录从队列搬进缓冲区(主线程)
SetFilter(filter)voidConsoleFilter.All/Log/Warning/Error
SetSearch(text)void按子串过滤
RegisterCommand(name, handler, help)void注册一个命令
ExecuteCommand(input)bool执行一行命令
IsOpenbool控制台是否已打开
OverlayHostOverlayHostPortal 容器
ToggleKeyKeyCode热键(默认 ~
MaxEntriesint环形缓冲区大小(500)
Instancestatic SusConsoleService单例

SusConsoleDriver

csharp
public class SusConsoleDriver : MonoBehaviour
{
    public SusConsoleService Service;

    private void Update()
    {
        if (Input.GetKeyDown(Service.ToggleKey))
            Service.Toggle();
        Service.DrainPendingEntries();
    }
}

SusLogEntry

叠加控制台的环形缓冲行(SusConsoleService 在拦截 Unity 日志后存储的内容)。不是进程级 gated logger SusLog / SusLogLevel — 见 11-api-reference

csharp
public struct SusLogEntry
{
    public LogType Type;        // Log, Warning, Error, Exception, Assert
    public string Message;      // Text
    public string StackTrace;   // Stack (for errors)
    public float Time;          // Time.unscaledTime
}

ConsoleFilter

csharp
public enum ConsoleFilter { All, Log, Warning, Error }

6. 测试

文件测试数覆盖内容
sus-core/Runtime/Tests/SusConsoleServiceTests.cs10 playmodeShow/Hide/Toggle、日志拦截、缓冲区溢出、过滤、搜索、RegisterCommand/ExecuteCommand、Clear