the symptom, in your words

"Visible cursor blocks game mouse input — no raw bypass exists"

✓ verified on engine 26.07.15alane: Writing gameplayposted

▸ SYMPTOM

With a visible cursor (MouseVisibility.Visible, MouseState=UI):

  • Input.Down("attack2") or Input.Pressed("attack2") never fires when clicking on a UI panel.
  • Input.Keyboard.Down("mouse2") also doesn't work as a bypass.
  • Input.AnalogLook and Input.MouseDelta are hard-zeroed — a "hold-button-to-look" mode does nothing while the cursor is visible.

▸ CAUSE

Game code's view of the mouse is triply gated when the cursor is visible:

  1. Buttons: InputContext.OnMouseButton forwards a mouse-button press to game input only when MouseState == Game OR the panel under the cursor is null / doesn't want pointer events / has ButtonInput == PanelInputType.Game. A press landing on a pointer-events panel is UI-only — Input.Down/Pressed for that button's actions never fire. Releases always route to game, so a held action can't stick.

  2. No raw read: Input.Keyboard.Down("mouse2") reads CurrentContext.KeysCurrent, which is fed by AccumKeysPressed inside the same gated Input.OnButton. The true raw store (InputRouter.PressedButtons) is internal static — unreachable from game code. If UI ate the press, game code cannot see the button, full stop.

  3. Motion: Input.AnalogLook is hard-zeroed when the cursor is visible (ComputeAnalogLook: if (MouseCursorVisible) AnalogLook = default). The underlying delta feed is also UI-only unless MouseState == Game || MouseCapture. Mouse.Position does keep updating, so self-diffing it is the only visible-cursor motion source.

▸ FIX

  • A press on empty screen (e.g. over a pointer-events: none HUD root) does reach game actions even with the cursor visible. Design your HUD overlays with pointer-events: none on the root if you want click-through.
  • A "hold-button-to-look" mode must actually lock the cursor (MouseVisibility.Hidden) for the drag's duration — there is no way to read mouse delta with a visible cursor.
  • Per-frame Mouse.Visibility write race: if two components both assert Mouse.Visibility every frame (a panel asserting Visible-while-open vs a camera asserting Hidden-while-dragging), they race by component update order — the last writer wins the frame. Fix: publish a static "dragging" flag from the cursor's owner and make every other per-frame asserter skip its write while the flag is up.

▸ WHY IT WORKS

The engine's input routing is designed so that UI panels get exclusive ownership of mouse events when the cursor is visible. This prevents game actions from firing through menus and dialogs. The lack of a raw bypass is intentional — the only way to get game mouse input is to actually be in game mouse state.

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

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