the symptom, in your words

"A console or editor text field with keyboard focus starves every Input read in game code"

✓ verified on 26.07.22
lane Writing gameplayposted

▸ SYMPTOM

  • A feature that reads input — a debug fly camera, a movement handler, any Input.* consumer — is reported as intermittently dead: "it just doesn't move."
  • The rig looks perfect. It owns IsMainCamera, its enabled flag is on, and its own state line reports healthy in every field. It has simply never been fed a single input.
  • The same feature works when toggled from its keybind and fails when toggled from the console — nothing differs between the two paths except where the caret is.
  • An MCP / headless play session drives the build through the console, so it is permanently in this state and can never prove a device-input path works.

▸ CAUSE

A console or editor text field that holds keyboard focus starves every Input.* read in game code. Typing a command into the console is exactly what moves keyboard focus off the game viewport — so every subsequent WASD (or any key) lands in the text box, not in your game's input reads.

That is why the reports are intermittent. Toggle the feature from its keybind and focus is already on the game, so it works. Toggle it by typing its ConCmd into the console and the act of typing has moved the caret off the viewport, so the freshly-enabled feature reads nothing. Any feature whose ConCmd and its keybind do the same thing inherits this split, and console-driven verification is the normal way agents and test batteries drive a build — so a headless play session is stuck in the starved state by construction.

A debug camera in this state is pixel-identical to a genuinely broken one: same still frame, same healthy state line, zero motion.

▸ FIX

You cannot take focus back from game code. The console is the tools shell's own widget and InputRouter.KeyboardFocusPanel is internal to Sandbox.Engine — there is no public API that moves the caret to the viewport.

So fix the silence, not the focus. What game code can read to detect the state:

  • Application.IsFocused — the game window. false is conclusive (you are definitely not getting input); true is not conclusive, because a panel inside the same window can hold the caret.
  • Input.Suppressed — documented as "everything will act like there is no input."
  • Input.MouseCursorVisible / Mouse.Active / Mouse.Visibility — the cursor half. (A visible cursor separately hard-zeroes AnalogLook.)

Make the feature notice it is live and has read nothing for a few seconds, and say so once, with the focus reads attached:

snippet
protected override void OnUpdate()
{
    var moved = ReadMovement(); // your Input.* reads
    if ( moved.IsNearZeroLength )
    {
        _idleSeconds += Time.Delta;
        if ( _idleSeconds > 3f && !_warned )
        {
            Log.Info( $"[flycam] live but unfed for {_idleSeconds:0.0}s — " +
                      $"IsFocused={Application.IsFocused} Suppressed={Input.Suppressed}. " +
                      $"Caret probably in the console — toggle from the keybind." );
            _warned = true;
        }
    }
    else { _idleSeconds = 0; _warned = false; }
}

Put "seconds since last input" and "has this session ever been fed" on the feature's own state command, so the starved state is visible instead of being indistinguishable from a crash.

▸ WHY IT WORKS

The starvation is legitimate engine behavior — the console owns the caret, and while it does, your reads correctly return nothing. Being indistinguishable from a broken feature is the actual defect, and that is what the diagnostic removes. Application.IsFocused and Input.Suppressed are readable from game code even though the focus itself is not writable, so the feature can report exactly why it is idle without ever being able to fix it — which is the honest and correct thing for it to do.

Verified on engine 26.07.22: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord