Timing guide: input read in FixedUpdate or OnGUI

Why input read in FixedUpdate or while OnGUI handles events arrives one frame later after migration, and the small change that removes the delay.

Documentation pages

Input Porter is coming soon to the Unity Asset Store. This guide describes the release being prepared.

Do you need this page? Only if the Scan Report’s Timing tab lists something. Nothing is broken. This page explains a small timing difference the migration brings, and a few-line change that removes it if your game needs exact timing.

The short version

  • Your code keeps working after the migration.
  • In two places, input now reaches your code one frame later than before, about 16 ms at 60 fps:
    • code in FixedUpdate;
    • code in OnGUI that reacts to key presses or mouse clicks.
  • Everywhere else nothing changes: Update, LateUpdate, coroutines, and OnGUI while it draws.
  • This comes from Unity’s Input System, not from Input Porter. It is how the Input System processes input, so code written for the Input System from scratch sees the same one-frame delay. Unity documents it in the Input System manual under Timing and latency.
  • Most games can’t tell the difference. If yours needs frame-exact controls (a jump in a physics platformer, a fighting game), each case below takes a few lines to fix. The same change also fixes a bug the old Input Manager had in those places.

A little background

If these terms are familiar, skip to Why this happens below.

  • The Input Manager is Unity’s original input system: Input.GetKey, Input.GetAxis("Horizontal"), and the axes under Project Settings > Input Manager. Unity has deprecated it.
  • The Input System is the package that replaces it. Input Porter moves your code onto it. Your calls become LegacyInput.GetKey and so on, which read the Input System and give the same results the Input Manager did.
  • Update runs once every frame. Most input code lives here, and it is not affected.
  • FixedUpdate runs on a fixed clock for physics, 50 times a second by default. Depending on the frame rate, it runs zero, one, or several times in a frame, always before Update.
  • OnGUI draws Unity’s older code-driven UI (IMGUI), often used for debug menus and in-game tools. It runs several times per frame, once for each event such as a key press, a click, or a redraw.

Why this happens

Unity’s old Input Manager read the keyboard at the very start of each frame. The Input System (the replacement Unity now requires) reads it just before Update, which is after FixedUpdate has already run. Input Porter can’t read input earlier than the Input System does without breaking any Input System code you already have, so code that runs before Update sees the previous frame’s input.

This is how the Input System works, not a limit of Input Porter: code written for the Input System from scratch sees the same thing. Unity’s Input System manual covers it under Timing and latency (see “Optimize for fixed update” and “Avoid missed or duplicate events”). Its advice is the pattern below: read input in Update, and pass it to FixedUpdate in a field.

The Input System has a setting that moves the delay rather than removing it: Project Settings > Input System Package > Update Mode > Process Events in Fixed Update. FixedUpdate then sees input on time, but Update doesn’t, and Unity’s manual notes it can add latency. Input Porter leaves this setting as it is.

What each finding means, and how to fix it

The scan reports one of these for each read. Each fix is the pattern Unity and its community already recommend for the old Input Manager too.

1. A press or release checked in FixedUpdate (GetButtonDown, GetKeyDown, GetMouseButtonUp, …)

After migration: the press is still detected exactly once, one frame later. Worth knowing: the old Input Manager could miss these presses entirely when several frames ran between physics steps. The fix removes the delay and that bug.

Record the press in Update, and act on it in FixedUpdate:

// Before
void FixedUpdate()
{
    if (LegacyInput.GetButtonDown("Jump")) body.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
}

// After
bool jumpRequested;

void Update()
{
    if (LegacyInput.GetButtonDown("Jump")) jumpRequested = true;
}

void FixedUpdate()
{
    if (jumpRequested)
    {
        body.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
        jumpRequested = false;
    }
}

2. A held key or an axis read in FixedUpdate (GetKey, GetButton, GetAxis, GetAxisRaw)

After migration: movement starts and stops one frame later. Most games can’t tell. Fix it if your controls must feel frame-exact:

// Before
void FixedUpdate()
{
    var move = new Vector3(LegacyInput.GetAxis("Horizontal"), 0f, LegacyInput.GetAxis("Vertical"));
    body.AddForce(move * speed);
}

// After
Vector3 move;

void Update()
{
    move = new Vector3(LegacyInput.GetAxis("Horizontal"), 0f, LegacyInput.GetAxis("Vertical"));
}

void FixedUpdate()
{
    body.AddForce(move * speed);
}

3. Input read in OnGUI (IMGUI debug menus and tools)

OnGUI runs several times per frame. While it’s handling keyboard and mouse events it runs before Update, and there the result is one frame late. While it’s drawing, it runs after Update and there’s no delay.

Either use the event IMGUI already hands you, which is exact:

void OnGUI()
{
    var e = Event.current;
    if (e.type == EventType.KeyDown && e.keyCode == KeyCode.F1) menuOpen = !menuOpen;
}

or read the key in Update and only draw in OnGUI, as in pattern 1.

Do I have to change anything?

No. Every finding keeps working after migration. Change it if you want frame-exact timing; the change is also better practice under the old Input Manager. In Input Porter, the Timing tab of the Scan Report lists every finding, and each row of the migration window’s To do by hand list names its section here (for example “guide 2”).