11 TextMode – Console Display

8 min read

Render the Scylla debug console through a TextMode view, so your developer console gets crisp SDF text, scrollback, selection and full-screen screens instead of a plain text field.

Summary

The Scylla Console module owns the developer console: commands, history, log capture, the performance monitor. What it does not own is how any of that looks on screen. That is a display, and this bridge is the TextMode implementation of one.

Turning it on means the console renders as a character grid rather than as a text field. In practice, that means crisp SDF text at any resolution, real terminal scrollback with drag and momentum, text selection you can copy out of, and the ability for a command to take over the whole console with a full-screen screen of its own. That last one is the interesting part: because the console is a grid, a command can draw an interactive interface into it rather than only printing lines.

The bridge is a separate assembly, ScyllaTextMode.ConsoleDisplay, and it registers itself with the console before the first scene loads. You do not wire anything up. If both modules are present, the TextMode display is available; if TextMode is absent, the console falls back to its own rendering and nothing breaks. That separation is why neither module has to depend on the other.

Use the console display when you want:

  • A developer console that stays legible at any resolution or display scale.
  • Scrollback the player or tester can drag through and copy from.
  • Commands that draw their own interactive screens rather than printing lines.
  • One consistent look between your game’s terminals and its debug console.

Features

  • Automatic registration. The bridge registers its display factory before scene load, so having the assembly in the project is the whole setup.
  • SDF text. The console gets the same distance-field rendering as everything else in TextMode, so it stays sharp at any size.
  • Scrollback with drag. Full terminal scrollback, drag scrolling and flick momentum, rather than a scroll rect over a text field.
  • Selection and copy. Drag to select and copy to the clipboard, exactly what a tester filing a bug needs.
  • Full-screen screens. IConsoleScreen lets a command push an interactive screen that owns the console until it pops.
  • Rich text handling. Markup in log lines is parsed into cell colors rather than rendered as literal tags.
  • Independent styling. The console names no theme, so its colors stay under the Console module’s control rather than moving with a game-wide retheme.

Screens

The part worth knowing about is IConsoleScreen. A normal console command prints and returns. A screen takes over.

HookWhen it runsWhat it is for
OnOpen(view, colors)When the screen is pushedYou get the UITextModeView and the console’s color scheme. Draw your initial state here.
OnUpdate(deltaTime)Every frame the screen is activeHandle input, redraw what changed. This is your game loop for the duration.
OnClose()When the screen is poppedRelease anything you took. The console restores what was underneath.

Screens stack, so a screen can push another and popping returns to the previous one. The module ships a tic-tac-toe command as a worked example of the pattern, which is a more useful reference than it sounds: it covers drawing, input, and teardown in about as little code as the pattern allows.

Recipes

Turn on the TextMode console display

Problem. You want the developer console to render through TextMode rather than the default display.

Solution. Have both modules in the project. There is no wiring step.

The bridge assembly registers its factory with the console using a before-scene-load hook, so the display is available as soon as the game starts. The Console module picks it up when it initializes. If you remove TextMode from the project, the console reverts to its own rendering rather than failing.

Write a command that draws its own screen

Problem. Your debug console needs an entity inspector: a list you can arrow through, with details for the selection. Printing that as lines is unusable, because every keypress reprints the whole thing.

Solution. Implement IConsoleScreen and push it from a command. The console hands you the view and gets out of the way.

public sealed class EntityInspectorScreen : IConsoleScreen
{
    private UITextModeView _view;
    private TextModeConsole _console;
    private int _selected;

    /* Called once when the screen is pushed. The color scheme comes from the Console module,
       so an inspector drawn with these colors matches the rest of the console. */
    public void OnOpen(UITextModeView view, DebugConsoleColorScheme colors)
    {
        _view = view;
        _console = new TextModeConsole(view.Grid, view.GlyphCache);
        Redraw();
    }

    /* Your loop for as long as the screen is up. Redraw only when something changed; a full
       redraw is cheap, but doing it every frame for a static screen is pointless. */
    public void OnUpdate(float deltaTime)
    {
        if (Keyboard.current.downArrowKey.wasPressedThisFrame)
        {
            _selected = Mathf.Min(_selected + 1, _entities.Count - 1);
            Redraw();
        }
    }

    /* The console restores what was underneath, so there is nothing to undraw. Release
       anything you allocated. */
    public void OnClose()
    {
        _console = null;
        _view = null;
    }
}

Keep the console’s colors out of your theme system

Problem. Your game lets the player pick a TextMode theme, and switching it repaints the debug console too, which is not what anyone wanted.

Solution. This is already the default, and it is worth knowing why. The console’s terminal names no theme, which the theme system reads as an opt-out, so the Console module keeps control of its own palette.

If you deliberately want the console to follow the game’s theme, that is a change to the console’s own color configuration rather than to the theme system.

API Sketch

/* Assembly: ScyllaTextMode.ConsoleDisplay, namespace Scylla.Console.TextMode */

interface IConsoleScreen {
    void OnOpen(UITextModeView view, DebugConsoleColorScheme colors);
    void OnUpdate(float deltaTime);
    void OnClose();
}

class TextModeDebugConsoleDisplay {
    void Initialize(); void Dispose();
    void SetVisible(bool visible);
    void AppendLine(...); void Clear();
    void Update(); void UpdateLayout(); void UpdateFont(...);

    bool PushScreen(IConsoleScreen screen);
    bool PopScreen();

    void ScrollBy(int lines); void ScrollTo(float position);
    void ScrollToTop(); void ScrollToBottom();
    void ApplyScrollVelocity(float velocity);

    void SelectAll(); void CopySelectionToClipboard();
    bool HandleSelectionInput(...);
    void HandleDragStart(...); void HandleDrag(...); void HandleDragEnd(...);

    void CLIFocus(); void CLIUnfocus(); void CLIClear(); void CLIProcessInput(...);
}

See the API reference for full signatures, remarks, and exception contracts on every member.

Best Practices

  • Let the registration do its job. The bridge wires itself up before scene load; adding manual setup on top invites double registration.
  • Redraw on change, not every frame, inside a screen. OnUpdate runs every frame, but a static inspector does not need repainting sixty times a second.
  • Use the color scheme you are handed. OnOpen gives you the console’s palette, so a screen drawn with it matches the rest of the console rather than announcing itself.
  • Pop what you push. Screens stack, and a screen that never pops leaves the console permanently occupied.
  • Release references in OnClose. The view outlives your screen, so holding one after close keeps an object alive that the console may have moved on from.
  • Prefer a screen to a wall of printed lines whenever the output is something the user navigates rather than reads once.
  • Keep the console’s styling separate from the game’s theme unless you have a reason to link them. A debug tool that changes appearance with the game’s art direction is harder to read at a glance.
  • Test with TextMode absent if you ship both modules separately. The console is designed to fall back, and that path deserves a run.
  • Do not draw into the console’s grid from outside a screen. In log mode the display rebuilds visible lines from scrollback whenever content is dirty, so anything written underneath is overwritten on the next render.
  • Treat the console as a developer surface. Selection, scrollback and screens are there to make debugging fast, not to be a shipping UI framework; the terminal frontend is what you build player-facing terminals on.

Pitfalls