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
OnGUIthat reacts to key presses or mouse clicks.
- code in
- Everywhere else nothing changes:
Update,LateUpdate, coroutines, andOnGUIwhile 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.GetKeyand so on, which read the Input System and give the same results the Input Manager did. Updateruns once every frame. Most input code lives here, and it is not affected.FixedUpdateruns 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 beforeUpdate.OnGUIdraws 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”).