Introduction
In the previous chapters you learned how Unity's physics components work: the Rigidbody,
colliders, physics materials, and the PhysX simulation engine running underneath it all. Now it is time
to connect that knowledge to mathematics. In this chapter you will learn how to apply forces, torques,
and impulses programmatically in C#, understand exactly when and where to write physics code, and build
two complete physics-based projects from scratch.
Physics programming sits at a fascinating intersection of math and game feel. The same jump can feel
floaty or snappy depending purely on which ForceMode you choose and how much mass your
Rigidbody has. A car can feel heavy and realistic or twitchy and arcade-like — the difference is often
a single line of code. Once you understand the math behind these choices, you will stop guessing and
start designing exactly the feel you want.
The AddForce() Method
The primary way to move a physics object in Unity is Rigidbody.AddForce(). This method
tells the physics engine to apply a force vector to the Rigidbody's center of mass at the next
simulation step. Unlike setting transform.position directly, using AddForce()
respects mass, drag, and all other physics properties — the result is physically plausible motion.
The basic signature is:
rb.AddForce(Vector3 force, ForceMode mode = ForceMode.Force);
The first argument is the force vector in world space. Its direction determines which
way the force pushes, and its magnitude determines how strong the push is. The second argument is the
ForceMode, which controls how the force is interpreted by the physics engine (we will cover
all four modes in detail shortly).
If you want to apply force in the object's local space instead of world space, use
AddRelativeForce():
rb.AddRelativeForce(Vector3 localForce, ForceMode mode = ForceMode.Force);
This is useful for things like spaceship thrusters: you always want to push "forward" relative to the ship, regardless of which way the ship is pointing in world space.
3D vs 2D Physics
Unity has two separate physics engines: the 3D engine (PhysX) and the 2D engine (Box2D). They use different components and slightly different APIs:
-
3D:
Rigidbodycomponent +AddForce(Vector3, ForceMode) -
2D:
Rigidbody2Dcomponent +AddForce(Vector2, ForceMode2D)
Note that ForceMode2D only has two values: ForceMode2D.Force and
ForceMode2D.Impulse. The Acceleration and VelocityChange modes are not available in 2D.
This is a limitation of Box2D and rarely matters in practice — the two available modes cover the vast
majority of use cases.
Building a Physics-Based Movement Controller
Let's build the most common use of AddForce(): a movement controller that reads player
input and pushes a Rigidbody around. Here is the complete MonoBehaviour:
using UnityEngine;
public class PhysicsMovement : MonoBehaviour
{
[SerializeField] private float moveForce = 10f;
[SerializeField] private float maxSpeed = 5f;
private Rigidbody rb;
private void Start()
{
// Cache the Rigidbody component once — GetComponent is expensive
rb = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
// Read input axes (returns values between -1 and 1)
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
// Build a force vector in world space (flat movement on X-Z plane)
Vector3 force = new Vector3(horizontal, 0f, vertical) * moveForce;
// Only apply force if we are under the speed cap
if (rb.velocity.magnitude < maxSpeed)
{
rb.AddForce(force, ForceMode.Force);
}
}
}
A few things to notice. First, we cache the Rigidbody in Start() rather than calling
GetComponent<Rigidbody>() every frame — component lookups have a non-trivial cost
and should always be cached. Second, the physics code is in FixedUpdate(), not
Update(). We will explain exactly why this matters in the next section.
Third, we check rb.velocity.magnitude before applying force. Without a speed cap the object
would accelerate indefinitely as long as a key is held down — you can also achieve the same effect by
tuning the Rigidbody's Drag property in the Inspector, which applies a damping force
proportional to velocity and naturally creates a terminal velocity.
Update vs FixedUpdate vs LateUpdate
Unity calls three different update methods on MonoBehaviours, and choosing the right one is critical for physics programming. Getting this wrong is one of the most common sources of bugs in beginner Unity projects.
Update()
Update() is called once per rendered frame. If the game runs at 60 fps, Update is called
60 times per second. If the frame rate drops to 30, it is called 30 times. The interval between calls
is not constant — it varies with the rendering workload. You can get the elapsed time
since the last frame with Time.deltaTime.
Use Update() for: reading input, updating UI, moving non-physics objects (Transform-based
movement), checking conditions, playing sounds in response to events.
FixedUpdate()
FixedUpdate() is called at a fixed time step, completely independent of the rendered frame
rate. The default fixed timestep is 0.02 seconds (50 times per second). You can change
this in Edit > Project Settings > Time > Fixed Timestep.
The key insight is that the physics engine advances its simulation in FixedUpdate() steps.
If you apply a force in Update(), you are applying it at irregular intervals — sometimes
twice before a physics step, sometimes zero times. This produces inconsistent, framerate-dependent
behavior. Always put physics code in FixedUpdate().
Inside FixedUpdate(), use Time.fixedDeltaTime (not Time.deltaTime)
if you need to scale values by the elapsed time — although for AddForce() Unity handles
the timestep scaling for you automatically.
LateUpdate()
LateUpdate() is called once per frame, but after all Update() calls
have finished. This guaranteed ordering makes it ideal for anything that needs to react to what happened
in Update — most commonly, camera movement.
Use LateUpdate() for: cameras that follow a target, post-processing Transform adjustments,
anything that must execute after other scripts have moved objects.
The Rule in Plain English
- Input reading →
Update() - Physics forces and velocity changes →
FixedUpdate() - Camera following →
LateUpdate()
The Flickering Problem and How to Solve It
Suppose you have a ball controlled by physics (Rigidbody + AddForce) and a camera that follows it. You write something like this in your camera script:
// WRONG — causes flickering!
private void Update()
{
transform.position = target.position + offset;
}
You press Play and immediately notice a jitter or flickering in the movement — especially visible at lower frame rates. What is happening?
The physics engine moves the ball in discrete FixedUpdate steps (every 0.02 seconds). The
camera moves every rendered frame (every ~0.016 seconds at 60 fps). These two timelines do not align.
Sometimes a frame is rendered between two physics steps, meaning the camera has moved but the ball
hasn't yet — and you see a momentary mismatch.
The fix is to move the camera in LateUpdate(), which runs after the physics step for that
frame has been applied and after all Update calls have settled:
using UnityEngine;
public class SmoothFollow : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 offset = new Vector3(0f, 5f, -10f);
[SerializeField] private float smoothSpeed = 8f;
// LateUpdate guarantees we run AFTER physics and AFTER all Update() calls
private void LateUpdate()
{
Vector3 desiredPosition = target.position + offset;
transform.position = Vector3.Lerp(
transform.position,
desiredPosition,
smoothSpeed * Time.deltaTime
);
transform.LookAt(target);
}
}
This pattern eliminates flickering completely. The Lerp smooths out any remaining micro-jitter by interpolating toward the target rather than snapping to it.
Force vs Impulse: The Four ForceModes
ForceMode determines how the force vector you pass to AddForce() is applied.
There are four options, each suited to different gameplay scenarios.
ForceMode.Force
This is the default mode. The force is applied continuously over time, scaled by the physics timestep and the object's mass. A heavier object (more mass) accelerates more slowly under the same force — exactly as in real physics (F = ma, so a = F/m).
Units: Newtons (kg·m/s²). Use for: engine thrust, gravity-like effects, wind, magnetic attraction, anything that pushes continuously over multiple physics steps.
// Sustained upward thrust — heavier objects rise slower
rb.AddForce(Vector3.up * thrustPower, ForceMode.Force);
ForceMode.Impulse
The force is applied as an instantaneous velocity change, scaled by the object's mass. The entire effect is applied in a single physics step. A heavier object receives a smaller velocity change for the same impulse magnitude.
Units: Newton-seconds (kg·m/s). Use for: jumps, explosions, bullet knockback, any sudden velocity change triggered by an event.
// Jump — called once on a button press, not every frame
rb.AddForce(Vector3.up * jumpStrength, ForceMode.Impulse);
ForceMode.Acceleration
Like Force, but mass is ignored. All objects receive the same acceleration
regardless of how heavy they are. Useful when you want consistent behavior across objects of different
masses, or when simulating effects that physically apply to all objects equally (like gravity, which
accelerates all objects at 9.81 m/s² regardless of mass).
// All objects accelerate identically, regardless of mass
rb.AddForce(Vector3.forward * boostAcceleration, ForceMode.Acceleration);
ForceMode.VelocityChange
Like Impulse, but mass is ignored. Directly adds the given vector to the
Rigidbody's velocity in a single step. This is the most direct control you can have over a Rigidbody's
speed without simply setting rb.velocity directly.
// Immediately adds 5 m/s forward regardless of mass
rb.AddForce(Vector3.forward * 5f, ForceMode.VelocityChange);
Choosing the Right ForceMode
-
Sustained thrust, engine, gravity-like →
ForceMode.Force(respects mass) -
Jump, explosion, bullet hit →
ForceMode.Impulse(respects mass) -
Consistent acceleration ignoring mass →
ForceMode.Acceleration -
Direct velocity addition ignoring mass →
ForceMode.VelocityChange
Acceleration and VelocityChange in Practice
The mass-ignoring modes are more useful than they might initially appear. Consider a game with enemies
of varying sizes — small goblins and large trolls. If you use ForceMode.Impulse for a
knockback effect, the troll (high mass) will barely budge while the goblin (low mass) flies across the
screen. This might be intentional and realistic, but often you want all enemies to react with the same
visual snappiness.
In that case, ForceMode.VelocityChange gives every enemy the same velocity addition
regardless of mass:
// Consistent knockback regardless of enemy size
void ApplyKnockback(Rigidbody target, Vector3 direction, float strength)
{
target.AddForce(direction * strength, ForceMode.VelocityChange);
}
Similarly, ForceMode.Acceleration is ideal for player characters in platformers where you
want snappy, responsive movement that feels the same whether the player is carrying a heavy object or
not. Many professional Unity platformers use this mode for player movement precisely because it
decouples the feel of control from the physics mass, allowing designers to tune mass for collision
behavior without affecting movement feel.
2D Physics with Rigidbody2D
Working with 2D physics in Unity follows almost identical patterns, with the main differences being:
- Use
Rigidbody2Dinstead ofRigidbody - Forces use
Vector2instead ofVector3 - Use
ForceMode2Dwith only.Forceand.Impulsemodes - Movement is on the X-Y plane (Z is depth/layering, not physics)
using UnityEngine;
public class PlatformerMovement2D : MonoBehaviour
{
[SerializeField] private float moveForce = 8f;
[SerializeField] private float jumpForce = 12f;
[SerializeField] private float maxSpeed = 4f;
private Rigidbody2D rb;
private bool isGrounded = false;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
}
private void FixedUpdate()
{
float horizontal = Input.GetAxis("Horizontal");
Vector2 force = new Vector2(horizontal * moveForce, 0f);
if (Mathf.Abs(rb.velocity.x) < maxSpeed)
{
rb.AddForce(force, ForceMode2D.Force);
}
}
private void Update()
{
// Read jump input in Update (event-driven), apply in physics
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb.AddForce(Vector2.up * jumpForce, ForceMode2D.Impulse);
isGrounded = false;
}
}
private void OnCollisionEnter2D(Collision2D col)
{
if (col.gameObject.CompareTag("Ground"))
isGrounded = true;
}
private void OnCollisionExit2D(Collision2D col)
{
if (col.gameObject.CompareTag("Ground"))
isGrounded = false;
}
}
Notice that we read input in Update() and use GetButtonDown — which returns
true only on the exact frame the button is pressed. If we checked this in FixedUpdate()
we might miss the press entirely (if it happened between two physics steps) or detect it twice (if
FixedUpdate runs more than once per frame). Input reading belongs in Update(); force
application belongs in FixedUpdate().
Exercise: The Flying Saucer
Let's put everything together with a complete mini-project: a hovering saucer that the player controls with WASD, moving by applying forces and rotating toward its direction of movement.
Project Setup
- Create a new 3D scene.
- Add a Plane as the ground (scale 10, 1, 10), add a PhysicsMaterial with zero friction.
- Add a Capsule (or import a saucer mesh) at position (0, 2, 0). Add a
Rigidbody. - Set Rigidbody Constraints: Freeze Position Y, Freeze Rotation X and Z (saucer stays flat).
- Attach the script below.
using UnityEngine;
public class FlyingSaucer : MonoBehaviour
{
[Header("Movement")]
[SerializeField] private float thrustForce = 15f;
[SerializeField] private float maxSpeed = 8f;
[SerializeField] private float dragCoefficient = 2f;
[Header("Rotation")]
[SerializeField] private float rotationSpeed = 5f;
private Rigidbody rb;
private Vector3 inputDirection;
private void Start()
{
rb = GetComponent<Rigidbody>();
}
private void Update()
{
// Read input in Update
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
inputDirection = new Vector3(h, 0f, v);
}
private void FixedUpdate()
{
// Apply thrust force
if (inputDirection.magnitude > 0.1f && rb.velocity.magnitude < maxSpeed)
{
rb.AddForce(inputDirection.normalized * thrustForce, ForceMode.Force);
}
// Apply manual drag on X-Z to stop sliding (we froze Y so vertical drag is irrelevant)
Vector3 flatVelocity = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
rb.AddForce(-flatVelocity * dragCoefficient, ForceMode.Force);
}
private void LateUpdate()
{
// Rotate toward movement direction smoothly
if (inputDirection.magnitude > 0.1f)
{
Quaternion targetRotation = Quaternion.LookRotation(inputDirection.normalized);
transform.rotation = Quaternion.RotateTowards(
transform.rotation,
targetRotation,
rotationSpeed * Time.deltaTime * 100f
);
}
}
}
This script demonstrates several important patterns: caching the Rigidbody, separating input reading (Update) from physics (FixedUpdate), applying manual drag to create responsive-feeling deceleration, and handling rotation in LateUpdate to avoid conflicts with the physics step.
The AddTorque() Method
Just as AddForce() applies a linear force to the center of mass, AddTorque()
applies a rotational force — a torque — around an axis. This causes the Rigidbody to angularly
accelerate without any translation (unless combined with forces).
rb.AddTorque(Vector3 torque, ForceMode mode = ForceMode.Force);
The torque vector follows the right-hand rule: if you point your right thumb along the vector, your
fingers curl in the direction of rotation. So a torque of Vector3.up spins the object
counterclockwise when viewed from above; -Vector3.up spins it clockwise.
AddRelativeTorque() works the same way but in local space:
rb.AddRelativeTorque(Vector3 localTorque, ForceMode mode = ForceMode.Force);
Example: Spinning a Wheel with Physics
using UnityEngine;
public class PhysicsWheel : MonoBehaviour
{
[SerializeField] private float torqueStrength = 20f;
[SerializeField] private float maxAngularVelocity = 15f;
private Rigidbody rb;
private void Start()
{
rb = GetComponent<Rigidbody>();
// Increase Unity's default angular velocity cap (default is 7 rad/s)
rb.maxAngularVelocity = maxAngularVelocity;
}
private void FixedUpdate()
{
float input = Input.GetAxis("Vertical");
// Apply torque around the wheel's local X axis
rb.AddRelativeTorque(Vector3.right * input * torqueStrength, ForceMode.Force);
}
}
Note rb.maxAngularVelocity — Unity caps angular velocity at 7 rad/s by default to prevent
simulation instability. If your wheel or spinning object needs to go faster, raise this limit explicitly.
AddForceAtPosition()
There is a third force method worth knowing: rb.AddForceAtPosition(force, worldPosition).
This applies a force at a specific world-space point on the Rigidbody rather than at its center of
mass. If the point is off-center, the force will produce both translation and rotation — exactly like
pushing a real object off its center. This is perfect for simulating things like wind pushing on one
side of a sail, or a rocket thruster mounted on the edge of a ship.
// Push at the right edge — causes translation AND rotation
Vector3 rightEdge = transform.position + transform.right * 2f;
rb.AddForceAtPosition(Vector3.forward * pushStrength, rightEdge, ForceMode.Force);
Exercise: Roll-A-Ball Again
Let's rebuild the classic Unity Roll-A-Ball tutorial from scratch using everything in this chapter. The ball rolls on a plane, collects pickup objects, and displays a score.
Project Structure
- A plane at the origin — scale (2, 1, 2) for a comfortable play area
- A sphere at (0, 0.5, 0) with a Rigidbody — this is the player ball
- 8–12 small cube "collectibles" placed around the plane, each with a trigger collider and tag "Pickup"
- A UI Text element for the score
Ball Movement Script
using UnityEngine;
using UnityEngine.UI;
public class RollABall : MonoBehaviour
{
[Header("Physics")]
[SerializeField] private float rollForce = 12f;
[SerializeField] private float maxSpeed = 6f;
[Header("UI")]
[SerializeField] private Text scoreText;
private Rigidbody rb;
private int score = 0;
private Vector3 inputForce;
private void Start()
{
rb = GetComponent<Rigidbody>();
UpdateScoreUI();
}
private void Update()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
inputForce = new Vector3(h, 0f, v) * rollForce;
}
private void FixedUpdate()
{
if (rb.velocity.magnitude < maxSpeed)
{
rb.AddForce(inputForce, ForceMode.Force);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Pickup"))
{
other.gameObject.SetActive(false);
score++;
UpdateScoreUI();
if (score >= 12)
{
Debug.Log("You win!");
}
}
}
private void UpdateScoreUI()
{
scoreText.text = "Score: " + score;
}
}
Rotating Pickup Script
using UnityEngine;
// Attach to each collectible cube — makes them spin visually
public class PickupRotate : MonoBehaviour
{
[SerializeField] private float rotateSpeed = 90f;
private void Update()
{
// Non-physics rotation: purely visual, no Rigidbody needed
transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
}
}
The collectible rotation uses transform.Rotate() rather than physics — it is purely visual
and does not need to interact with the physics engine. This is a good pattern: use physics only where
it is genuinely needed, and Transform-based manipulation for purely cosmetic effects.
Putting It All Together: Physics Design Principles
Before moving to the next chapter, let's summarize the key principles you have learned:
-
Cache your Rigidbody. Always store the reference in
Start(). Never callGetComponent<Rigidbody>()in Update or FixedUpdate. - Physics code goes in FixedUpdate. No exceptions. Even if it "works" in Update on your machine, it will break on machines with different frame rates.
- Camera following goes in LateUpdate. This eliminates jitter when following physics objects.
- Use ForceMode deliberately. Force and Impulse respect mass; Acceleration and VelocityChange ignore it. Match your mode to your gameplay intent.
- AddTorque for rotational forces. Use AddRelativeTorque when the axis is in local space. Remember to raise maxAngularVelocity if needed.
- AddForceAtPosition for off-center forces. This produces both translation and rotation, simulating realistic physical interactions.
In the next chapter we will look at raycasting — casting invisible rays through the physics world to detect collisions, implement shooting, and solve ground-detection problems that collision callbacks alone cannot handle cleanly.
Need help with Unity3D development?
I'm a senior developer with 16+ years experience, including AAA projects at Ubisoft. Let's discuss how I can help with your game or interactive project.
Start a Conversation