Quick answer: Unity calls FixedUpdate as many times as needed to match Fixed Timestep to real time. At low frame rates this can be 3–10 calls per frame. Cap via Maximum Allowed Timestep, and never put input reads or spawn logic in FixedUpdate — use Update.
Here is how to fix Unity FixedUpdate called multiple times per frame. You put bullet spawning in FixedUpdate because you wanted physics-synced shots. On a fast machine at 120 FPS, one bullet per trigger. On a slower machine at 30 FPS, two bullets per trigger. On a hot-loaded build, six bullets. Same click, different results. Unity’s FixedUpdate runs according to physics timing, not frame timing, and the divergence between the two is where bugs live.
The Symptom
Code in FixedUpdate runs inconsistently depending on frame rate. Examples:
- Input reads from
Input.GetKeyDownin FixedUpdate returning true multiple times per physical press (or zero times) - Bullets spawned in FixedUpdate producing clusters of shots on low-framerate machines
- Counters incremented in FixedUpdate growing faster than expected on slow systems
- Physics feeling “jerky” when frame rate is low because of large physics catch-up
What Causes This
Fixed timestep is real time. Unity’s default Fixed Timestep is 0.02 (50 FPS physics). Every frame, Unity calls FixedUpdate enough times to cover elapsed real time. At 60 FPS render, every frame takes 16.67 ms, which is less than one fixed timestep — FixedUpdate runs 0 or 1 times per frame (alternating). At 30 FPS render, every frame takes 33 ms — FixedUpdate runs 1 or 2 times per frame. At 10 FPS, 5 times.
Maximum Allowed Timestep caps the catch-up. If a frame takes longer than Maximum Allowed Timestep (default 0.333 s), Unity stops catching up physics and accepts that physics will be “behind” real time. This prevents the spiral of death where slow frames cause more physics updates, which cause slower frames, etc.
Input APIs work in Update. Input.GetKeyDown, Input.GetKeyUp are evaluated once per frame based on WAS the key pressed/released since the last frame. Calling them in FixedUpdate returns true in one FixedUpdate call and false in the next (which might be the same frame). Reading input in FixedUpdate is never correct.
Spawning and one-shot logic. Anything that should happen “once per event” should be in Update or event callbacks, not FixedUpdate. FixedUpdate is for continuous physics forces (thrust, drag) that benefit from being applied per-physics-step.
The Fix
Step 1: Move input to Update.
using UnityEngine;
public class CorrectInput : MonoBehaviour
{
private bool wantsJump = false;
void Update()
{
// Detect the edge here, once per frame
if (Input.GetKeyDown(KeyCode.Space))
wantsJump = true;
}
void FixedUpdate()
{
if (wantsJump)
{
GetComponent<Rigidbody>().AddForce(
Vector3.up * 5f, ForceMode.Impulse);
wantsJump = false;
}
}
}
Update detects the press edge once per key stroke. FixedUpdate consumes the flag atomically and applies the force. Works correctly at any frame rate.
Step 2: Cap Maximum Allowed Timestep. Project Settings > Time. Default Maximum Allowed Timestep is 0.333 s — very generous. Lower to 0.1 s (physics catches up at most 5 steps per frame at 0.02 timestep). This prevents runaway physics on hitching frames:
- Fixed Timestep: 0.02 (50 Hz) or 0.0166 (60 Hz) or 0.0333 (30 Hz)
- Maximum Allowed Timestep: 0.1 (caps at 5 physics steps per frame)
On a frame that takes 500 ms (5 Hz), FixedUpdate runs 5 times instead of 25 times. Physics lags real time slightly but CPU does not spiral.
Step 3: Never spawn in FixedUpdate. Move any Instantiate calls triggered by user input or events to Update. Physics forces, velocity changes, Rigidbody.MovePosition, joint manipulation — those belong in FixedUpdate. Object creation and deletion do not.
Step 4: Use Time.fixedDeltaTime consistently. Inside FixedUpdate, use Time.fixedDeltaTime (always the same value). Inside Update, use Time.deltaTime. Mixing them produces incorrect math:
// In FixedUpdate
velocity += acceleration * Time.fixedDeltaTime;
// In Update
position += velocity * Time.deltaTime;
Interpolation for Smooth Rendering
If physics runs at 50 Hz but rendering at 144 Hz, Rigidbody positions snap between physics steps. Enable Rigidbody Interpolation mode Interpolate or Extrapolate on the Rigidbody component. Interpolate visually smooths between the last two physics positions; Extrapolate predicts ahead. Use Interpolate for most gameplay objects to avoid lag.
Debug Visualization
Log FixedUpdate call counts per frame to see what is actually happening:
int fixedCount = 0;
void FixedUpdate()
{
fixedCount++;
}
void LateUpdate()
{
if (fixedCount > 1)
Debug.Log($"FixedUpdate ran {fixedCount}x this frame");
fixedCount = 0;
}
Add this to a persistent debug object during dev builds. Counts above 1 are normal at sub-50 FPS. Counts above 5 indicate the frame took very long; consider profiler investigation.
Spiral of Death
The classic PhysX spiral: a slow frame triggers more FixedUpdate calls; the extra calls make the frame slower; which triggers even more FixedUpdate calls. Maximum Allowed Timestep exists to break this loop. Without a reasonable cap, a single heavy frame can lock your game in a runaway physics state for seconds.
“FixedUpdate runs as often as physics needs it. You cannot fix that. What you can fix is putting the right code in the right method.”
Related Issues
For physics jitter specifically, see Unity Physics Jittery Movement. For frame rate debugging, Debugging Frame Rate Drops covers the diagnostic side.
Input in Update, forces in FixedUpdate, spawning in Update. Three rules cover most bugs.