the symptom, in your words

"Input.Pressed edges drop or double-fire inside OnFixedUpdate"

✓ verified on engine 26.07.08elane: Writing gameplayposted

▸ SYMPTOM

Edge-triggered inputs (Input.Pressed("jump"), Input.Pressed("fire"), gear shifts) behave unreliably: sometimes a keypress is ignored entirely, other times it fires twice in one moment. The problem worsens at low or fluctuating framerates and only affects presses read inside OnFixedUpdate.

▸ CAUSE

Input.Pressed(...) is frame-scoped: the engine sets the "pressed this frame" flag once per rendered frame and clears it at the end of that frame. OnFixedUpdate runs at a fixed rate (50 Hz by default), which means:

  • A frame with no fixed tick (framerate > tick rate): the press flag is set and cleared with no OnFixedUpdate to read it -- input lost.
  • A frame with multiple fixed ticks (framerate < tick rate): the same press flag is read by every tick in that frame -- input duplicated.

Level-triggered reads (Input.Down("handbrake")) are fine in OnFixedUpdate because they reflect the held state, not a one-frame edge.

▸ FIX

Latch the edge in OnUpdate (per-frame), then consume it in the fixed-step logic:

snippet
private bool _jumpLatched;

protected override void OnUpdate()
{
    // Latch the edge per-frame -- never lost, never duplicated
    if (Input.Pressed("jump"))
        _jumpLatched = true;
}

protected override void OnFixedUpdate()
{
    if (_jumpLatched)
    {
        _jumpLatched = false;  // consume it
        DoJump();
    }

    // Level-triggered reads are fine here
    if (Input.Down("forward"))
        ApplyThrottle();
}

If the same action can also come from a scripted source (e.g. a test pilot or AI input struct that holds a bit across ticks), edge-detect the combined request against a _prev bool so both sources shift exactly once:

snippet
bool wantShift = _shiftLatched || inputOverride.ShiftUp;
if (wantShift && !_prevShift)
    DoShiftUp();
_prevShift = wantShift;
_shiftLatched = false;

▸ WHY IT WORKS

The latch bridges the frame/tick timing mismatch. OnUpdate runs exactly once per frame (matching Input.Pressed's lifetime), so the latch captures the edge reliably. The fixed tick then reads and clears the latch on its own schedule -- a frame with no tick keeps the latch until the next one (no loss), and the clear-after-read ensures a frame with multiple ticks only fires once (no duplication).

Verified on engine 26.07.08e — 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