Summary

Rigidbody2D is the Unity component that makes a 2D GameObject participate in the physics simulation. It enables realistic movement driven by forces and velocities, and is required for OnCollisionEnter2D and OnTriggerEnter2D callbacks to fire on a moving object. There are three body types, each suited to a different role. Physics-related code belongs in FixedUpdate, which runs on a fixed timestep independent of frame rate.


Key ideas

Body types:

Body typeBehaviourTypical use
DynamicFully simulated — responds to forces, gravity, and collisionsMoving enemies, projectiles, falling objects
KinematicMoves via script only (MovePosition or linearVelocity); not affected by forces or gravity. Behaves like an immovable object in collisions. By default its colliders only make contacts with Dynamic bodies, not with Static or other Kinematic ones, unless Use Full Kinematic Contacts is enabled; even then the contacts are reported but nothing pushes it backMoving platforms; players with a custom character controller that does its own collision checks
StaticDoes not moveWalls, floors, static hazards

Gravity Scale: How strongly gravity pulls the object. Set to 0 for top-down 2D games or for kinematic players that manage their own movement.

Freeze Rotation Z: Prevents the physics engine from rotating the object when it collides. Almost always enabled on player characters — a player that tips over when touching a wall is rarely intentional.

FixedUpdate(): The correct place for all physics and Rigidbody2D calls. It runs on Unity’s fixed timestep (default 50 times per second), independent of frame rate. Calling physics in Update causes frame-rate-dependent behaviour — the physics will feel different on machines with different performance.

rb.linearVelocity (Unity 6) / rb.velocity (older Unity): Direct velocity assignment gives crisp, predictable movement, which is why it is the usual choice for responsive character control. The trade-off with AddForce is about control versus momentum:

  • Setting linearVelocity gives instant, exact speed every step, but it overwrites whatever velocity physics or other code produced: a knockback impulse, an explosion or a conveyor belt is cancelled on the next FixedUpdate unless you skip the assignment while it plays out (the knockback example below does this with an isKnockedBack window).
  • AddForce (continuous force, the default ForceMode2D.Force) adds to the current velocity, so the body builds up speed, keeps momentum and combines naturally with other forces. That suits vehicles, spaceships, ice and deliberately floaty movement, but you have to cap the speed and add damping or counter-force yourself, and the response is less immediate.

(An earlier version of this page said AddForce “introduces acceleration drift”; that is not a defined term, and the trade-off above is the accurate version.)

AddForce(direction * magnitude, ForceMode2D.Impulse): Applies the entire force in a single physics step. Use for one-shot impulses: knockback, jump, explosion.

Vector2.normalized: Returns a direction vector with a magnitude of exactly 1. Normalising before multiplying by speed prevents diagonal movement from being faster than horizontal or vertical movement (diagonal magnitude is √2 ≈ 1.41 without normalisation).

Vector2.ClampMagnitude(velocity, maxSpeed): Limits the magnitude of a vector without changing its direction. Use as a speed cap to prevent velocity accumulating beyond a safe maximum.


In practice

Caching Rigidbody2D in Awake:

private Rigidbody2D rb;
 
private void Awake()
{
    rb = GetComponent<Rigidbody2D>();
    if (rb == null) Debug.LogError("[PlayerPhysics] No Rigidbody2D found!");
}

Physics movement in FixedUpdate:

private Vector2 moveInput;
 
private void Update()
{
    float h = Input.GetAxisRaw("Horizontal");
    float v = Input.GetAxisRaw("Vertical");
 
    // Store input for the next physics step
    moveInput = new Vector2(h, v).normalized;
}
 
private void FixedUpdate()
{
    HandleMovement();
    EnforceSpeedCap();
}
 
private void HandleMovement()
{
    // Direct velocity assignment — crisp, predictable
    rb.linearVelocity = moveInput * moveSpeed;
}
 
private void EnforceSpeedCap()
{
    // Preserves direction while clamping magnitude
    rb.linearVelocity = Vector2.ClampMagnitude(rb.linearVelocity, maxSpeed);
}

Read this as two clocks working together. Update runs once per rendered frame and records the latest player input. FixedUpdate runs on Unity’s physics timestep and applies that stored input to the Rigidbody2D. This separation is more reliable than mixing all input and physics work into one method, especially when a project grows beyond held movement keys.

One-shot input (jump): read in Update, consume in FixedUpdate:

Held axes can simply be stored each frame, as above. A button press is different. Input.GetButtonDown is true only during the frame the button went down, and Unity says to call it from Update “since the state gets reset each frame” (Unity Technologies, Scripting API: Input.GetButtonDown). FixedUpdate, meanwhile, “may be called zero, one, or multiple times per frame” (Unity Technologies, Scripting API: MonoBehaviour.FixedUpdate). So a press read inside FixedUpdate is lost on any frame that has no physics step. The fix is a flag that Update sets and FixedUpdate clears:

[SerializeField] private float jumpForce = 8f;
private bool jumpRequested;
 
private void Update()
{
    if (Input.GetButtonDown("Jump"))
        jumpRequested = true;      // remember the press until physics can use it
}
 
private void FixedUpdate()
{
    if (jumpRequested)
    {
        jumpRequested = false;     // consume it, so one press = one jump
        rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
    }
}

Two details matter. Only ever set the flag to true in Update (writing jumpRequested = Input.GetButtonDown("Jump"); would overwrite an unconsumed press with false on the next frame if no physics step ran in between). And clear it in FixedUpdate even if the jump is refused (for example, not grounded), unless you deliberately want a short jump buffer, which is a game-feel choice. The same rule applies to the Input System: check WasPressedThisFrame() in Update and store the result (see unity-input-system).

Does storing input in Update make movement inconsistent? Not the speed: FixedUpdate always applies the latest stored input over a fixed timestep, so the distance travelled per second is the same on any machine. What can look inconsistent is the smoothness, because the screen is drawn more often than physics moves the body. That is what interpolation fixes.


Smooth motion: Rigidbody2D interpolation

Physics runs at a fixed rate (50 steps per second by default) while frames are drawn at whatever rate the machine manages, often 60 or 144 per second. Some frames get no physics step and some get two, so a body moved by physics can appear to advance in uneven hops. Unity’s 3D Rigidbody reference explains that “Physics simulation runs at discrete timesteps, while graphics are rendered at variable frame rates”, which “can lead to visual jitter”, and that the effect “is particularly noticeable on GameObjects that the camera follows” (Unity Technologies, Scripting API: Rigidbody.interpolation). A follow camera moving every frame makes the player’s stutter obvious against a smoothly scrolling background.

Set Interpolate on the Rigidbody2D component (Inspector field Interpolate, or rb.interpolation in code):

SettingWhat it doesUse for
None (default)Draws the body exactly where the last physics step left itMost objects, especially ones the camera does not follow
Interpolate”Motion is smoothed based on the object’s positions in previous frames” (Unity Technologies, Scripting API: Rigidbody2D.interpolation)The player and anything the camera follows: the usual choice
Extrapolate”Smooths motion based on an estimate of its position in the next frame” (same source)Rarely; because it predicts, it can briefly overshoot when the body stops or bounces (this wiki’s caution)
private void Awake()
{
    rb = GetComponent<Rigidbody2D>();
    rb.interpolation = RigidbodyInterpolation2D.Interpolate;   // or set it in the Inspector
}

Guidance and gotchas:

  • Unity recommends turning interpolation on “for the main character but disable it for everything else” (Unity Technologies, Scripting API: Rigidbody.interpolation). The 3D page says this; the 2D behaviour is the same in principle.
  • Interpolation only smooths what physics moves. Moving an interpolated body by setting transform.position fights the physics system; when interpolation is enabled “the physics system takes control of the Rigidbody’s transform” (same source). Move it with linearVelocity, AddForce or MovePosition.
  • If jitter remains, check that the camera follows in LateUpdate (or uses Cinemachine) rather than in FixedUpdate; for pixel art, see also unity-pixel-perfect-camera.

MovePosition vs setting velocity

Both are valid ways to turn input read in Update into physics movement, but they suit different body types. In both cases, read input in Update, store it, and apply it in FixedUpdate, never in Update.

rb.linearVelocity = input * speedrb.MovePosition(rb.position + input * speed * Time.fixedDeltaTime)
Body typeDynamicKinematic: “MovePosition is intended for use with kinematic rigidbodies” (Unity Technologies, Scripting API: Rigidbody2D.MovePosition)
What it setsA speed; the solver works out the position and resolves collisionsA target position; Unity calculates the velocity needed to reach it “during the next physics update” (same source)
CollisionsThe body is pushed back by walls and pushes other Dynamic bodiesA Kinematic body is not stopped by what it hits; it pushes Dynamic bodies out of the way and does not collide with Static walls by default (see body types above)
Gravity and other forcesGravity still applies to the axis you do not overwrite (keep rb.linearVelocity.y for a platformer)Neither gravity nor linear damping affects the body during the move (same source); you write any falling yourself
Calling it twice per stepThe last assignment winsHas no cumulative effect: only the last target is used at the next physics update (same source)
Typical usePlayer characters, enemies, anything that should bump into the worldMoving platforms, lifts, scripted paths, character controllers that do their own collision queries (for example with Rigidbody2D.Cast)

Rule of thumb: if you want the physics engine to stop you at walls, use a Dynamic body and set velocity; if you want an exact path and will handle collision yourself, use a Kinematic body and MovePosition. Unity also notes that Rigidbody2D.MovePosition is meant for “relatively small distance movements”, because 2D bodies have a speed limit per step; to teleport, set rb.position instead.

3D Rigidbody works the same way. Unity 6 renames velocity to Rigidbody.linearVelocity in 3D too, and Rigidbody.MovePosition “Moves the kinematic Rigidbody towards position” and “complies with the interpolation settings”; for an instant jump to a new place, “use Rigidbody.position instead of MovePosition” (Unity Technologies, Scripting API: Rigidbody.MovePosition). Use the 3D CharacterController instead when you want neither (see unity-character-controller).


Knockback impulse:

public void ApplyKnockback(Vector2 sourcePosition)
{
    // Direction: away from the damage source
    Vector2 direction = ((Vector2)transform.position - sourcePosition).normalized;
 
    rb.linearVelocity = Vector2.zero;  // clear existing velocity for clean impulse
    rb.AddForce(direction * knockbackForce, ForceMode2D.Impulse);
}

Invoke for timed callbacks (e.g. ending a knockback window):

isKnockedBack = true;
Invoke("EndKnockback", knockbackDuration);  // calls EndKnockback() after N seconds
 
private void EndKnockback()
{
    isKnockedBack = false;
}

Inspector setup checklist

For a typical 2D player character:

  • Body Type: Dynamic (if physics should stop it at walls; the usual beginner choice and the one this page’s examples use) or Kinematic (if a custom controller handles collision itself; tick Use Full Kinematic Contacts if it must get collision callbacks from Static tilemaps and walls)
  • Gravity Scale: 0 for top-down; keep default for platformer
  • Freeze Rotation → Z: enabled (prevents tipping on collision)
  • Attach the Rigidbody2D to the same GameObject as the movement script

Gotchas

  • Never use transform.Translate or set transform.position directly on a Dynamic Rigidbody2D — this bypasses the physics engine and causes jitter, missed collisions, and tunnelling. Use rb.linearVelocity, AddForce, or (for Kinematic) rb.MovePosition.
  • Read input in Update; apply it in FixedUpdate. Calling Input.GetAxisRaw in FixedUpdate is often acceptable for held movement axes in small beginner demos, but it can miss one-frame button events such as GetButtonDown on frames with no physics step. Use the one-shot flag pattern above.
  • A Kinematic player walks through Static walls. With Use Full Kinematic Contacts off, “a Kinematic Rigidbody 2D will only collide with Dynamic Rigidbody 2Ds”; turning it on enables contacts with all body types, but a Kinematic body still gets no collision response, so your script must stop it (Unity Technologies, Unity Manual: Kinematic Body Type reference; Scripting API: Rigidbody2D.useFullKinematicContacts). The Inspector label reads Full Kinematic Contact in some Unity 6 versions. The same Manual page notes that “Trigger Colliders are an exception to this rule”, so trigger overlaps are still reported.
  • rb.linearVelocity is the Unity 6 API name. In older Unity versions (pre-6), the property is rb.velocity. Both refer to the same thing.

Worked example

The first script in the beginner 2D route is the movement pattern from this page as a complete component:

  • TopDownPlayerMovement.cs — [RequireComponent(typeof(Rigidbody2D))], caches the body in Awake, reads normalised GetAxisRaw input in Update and sets rb.linearVelocity in FixedUpdate; the folder’s README has the scene set-up (Gravity Scale 0, Freeze Rotation Z)

Practice

Build a top-down player that moves with physics and has a one-shot dash:

  • a Dynamic Rigidbody2D with Gravity Scale 0 and Freeze Rotation Z ticked
  • held movement read in Update, normalised, and applied with rb.linearVelocity in FixedUpdate
  • Space requests a dash: set a flag in Update, then in FixedUpdate clear it and apply AddForce(direction * dashForce, ForceMode2D.Impulse) in the current movement direction; while a short dash window runs (a bool cleared with Invoke, as in the knockback example), skip the velocity assignment and the speed cap so they do not cancel the impulse
  • a camera that follows the player in LateUpdate

Success test:

  • diagonal movement is no faster than straight movement
  • every press of Space produces exactly one dash, and holding it does not repeat
  • the player does not spin when it bumps a wall
  • you can describe the difference between Interpolate None and Interpolate on the followed player against a patterned background

Extension:

  • move the GetButtonDown check into FixedUpdate, raise the frame rate (for example by turning off VSync), and count how many presses are lost; then restore the flag pattern

Self-test

  1. Why does Rigidbody2D movement code go in FixedUpdate rather than Update?
  2. Why normalise the input vector before multiplying by speed?
  3. When would you use AddForce(..., ForceMode2D.Impulse) instead of setting linearVelocity?
  4. Why can Input.GetButtonDown inside FixedUpdate miss a jump, and what is the fix?
  5. Why should the jump flag only ever be set to true in Update, never assigned straight from GetButtonDown?
  6. A player moved by physics jitters when the camera follows it. Which Rigidbody2D setting fixes this, and why does the jitter happen?

Answers

  1. FixedUpdate runs on Unity’s fixed physics timestep, independent of frame rate, so physics behaves the same on every machine.
  2. Without it, diagonal input has a magnitude of about 1.41, so diagonal movement would be faster than straight movement.
  3. For one-shot forces such as knockback, a jump or an explosion; velocity assignment suits continuous character movement.
  4. GetButtonDown is true only during the frame of the press, and FixedUpdate can run zero times in a frame, so the press can fall on a frame with no physics step. Set a flag in Update and consume it in FixedUpdate.
  5. Assigning it directly would overwrite an unconsumed press with false on the next frame if no physics step ran in between.
  6. Interpolate. Physics moves the body at a fixed rate while frames are drawn at a different rate, so some frames show no movement and some show two steps.