Game Physics Formulas — Cheat Sheet
All essential equations for building real-time physics engines: impulse-momentum, collision response with friction, spring-damper systems, rigid body dynamics, inertia tensors for common shapes, and numerical integration methods compared.
1. Kinematics and Newton's Laws
F = m·a a = F/m
Newton's 2nd Law (rotational):
τ = I·α α = I⁻¹·τ
Kinematic integration (per timestep dt):
v += a·dt
x += v·dt
Kinetic energy:
KE_trans = ½·m·v²
KE_rot = ½·ω·I·ω
Momentum:
p = m·v (linear)
L = I·ω (angular)
| Symbol | Meaning | SI Unit |
|---|---|---|
m |
mass | kg |
F |
force | N = kg·m/s² |
a |
acceleration | m/s² |
v |
velocity | m/s |
x |
position | m |
τ |
torque | N·m |
I |
moment of inertia tensor | kg·m² |
α |
angular acceleration | rad/s² |
ω |
angular velocity | rad/s |
2. Impulse and Collision Response
When two rigid bodies collide at contact point P, we apply an impulse J along the contact normal n̂.
v_rel = (v_A + ω_A × r_A) − (v_B + ω_B × r_B)
v_n = dot(v_rel, n̂) (normal component, negative = approaching)
Impulse magnitude (frictionless):
j = −(1 + e) · v_n / K
Effective mass K:
K = 1/m_A + 1/m_B
+ dot((I_A⁻¹·(r_A × n̂)) × r_A, n̂)
+ dot((I_B⁻¹·(r_B × n̂)) × r_B, n̂)
Velocity update:
v_A' = v_A + (j/m_A)·n̂
ω_A' = ω_A + I_A⁻¹·(r_A × j·n̂)
v_B' = v_B − (j/m_B)·n̂
ω_B' = ω_B − I_B⁻¹·(r_B × j·n̂)
e = coefficient of restitution (0 = perfectly inelastic, 1 = elastic)
| Material pair | e (approx) |
|---|---|
| Steel on steel | 0.55–0.75 |
| Rubber ball on floor | 0.75–0.85 |
| Superball | 0.90–0.95 |
| Clay on clay | 0 (plastic impact) |
| Glass on glass | 0.95 |
| Golf ball | 0.68 |
| Basketball | 0.70–0.75 |
function resolveCollision(bodyA, bodyB, contact) {
const n = contact.normal; // unit normal A→B
const rA = contact.point.sub(bodyA.pos);
const rB = contact.point.sub(bodyB.pos);
const vRel = bodyA.vel.add(rA.cross(bodyA.angVel))
.sub(bodyB.vel).sub(rB.cross(bodyB.angVel));
const vn = vRel.dot(n);
if (vn >= 0) return; // separating — skip
const e = 0.7;
const rAxN = rA.cross(n), rBxN = rB.cross(n);
const K = bodyA.invMass + bodyB.invMass
+ rAxN.dot(bodyA.invInertia.mulVec(rAxN))
+ rBxN.dot(bodyB.invInertia.mulVec(rBxN));
const j = -(1 + e) * vn / K;
bodyA.vel = bodyA.vel.add(n.scale(j * bodyA.invMass));
bodyB.vel = bodyB.vel.sub(n.scale(j * bodyB.invMass));
bodyA.angVel = bodyA.angVel.add(bodyA.invInertia.mulVec(rAxN).scale(j));
bodyB.angVel = bodyB.angVel.sub(bodyB.invInertia.mulVec(rBxN).scale(j));
}
3. Friction and Contact Forces
|f_t| ≤ μ · |f_n|
If sliding: f_t = −μ_k · |f_n| · t̂ (kinetic friction)
If not sliding: |f_t| ≤ μ_s · |f_n| (static friction cone)
μ_s ≈ 1.2–1.4 × μ_k (static always slightly larger)
Impulse-based friction (tangential impulse):
v_t = v_rel − dot(v_rel, n̂)·n̂ (tangential relative velocity)
j_t = −dot(v_t, t̂) / K_t (tangential impulse)
Clamp: |j_t| ≤ μ·|j_n| (friction cone)
| Material pair | μ_s (static) | μ_k (kinetic) |
|---|---|---|
| Steel on steel (dry) | 0.74 | 0.57 |
| Steel on steel (greased) | 0.15 | 0.09 |
| Rubber on concrete (dry) | 0.8 | 0.75 |
| Wood on wood | 0.4 | 0.2 |
| Ice on ice | 0.03 | 0.02 |
| Teflon on Teflon | 0.04 | 0.04 |
friction_impulse = clamp(j_t, -μ·j_n, +μ·j_n). To
distinguish static/kinetic, check if tangential slip velocity exceeds
a small threshold (e.g. 0.01 m/s).
4. Spring-Damper Systems
F_spring = −k · (x − x_rest)
Damper (viscous):
F_damper = −c · v
Spring-damper combined:
F = −k·Δx − c·v
Critical damping:
c_critical = 2·√(k·m)
Damping ratio:
ζ = c / (2·√(k·m))
ζ < 1: underdamped (oscillates)
ζ = 1: critically damped (fastest return, no oscillation)
ζ > 1: overdamped (slow return)
Natural frequency:
ω_n = √(k/m) [rad/s]
f_n = ω_n / (2π) [Hz]
Rotational Spring (torsional)
Same damping ratio formula: ζ = c_r / (2·√(k_r · I))
// Spring-damper force between two connected bodies
function springDamper(posA, posB, velA, velB, restLength, k, c) {
const delta = posB.sub(posA);
const dist = delta.length();
if (dist < 1e-8) return Vec3.zero;
const dir = delta.scale(1/dist);
const stretch = dist - restLength;
const relVel = velB.sub(velA).dot(dir); // signed velocity along spring
const force = k * stretch + c * relVel;
return dir.scale(force); // apply to A; negate for B
}
5. Rigid Body Dynamics
Position: x ∈ ℝ³
Orientation: q ∈ SO(3) (quaternion preferred)
Linear mom: p = m·v
Angular mom: L = I_world · ω
World-space inertia tensor:
I_world = R · I_body · R^T
(R = rotation matrix from quaternion)
Equations of motion:
dx/dt = v = p/m
dp/dt = F_ext
dq/dt = ½ · Q(ω) · q (quaternion derivative)
dL/dt = τ_ext
ω = I_world⁻¹ · L
Force from angular velocity Coriolis (rotating frame):
Note: ω × (ω × r) is centripetal, ω × v is Coriolis — only needed in rotating reference frames.
Quaternion Integration
dq/dt = ½ · [0, ω] ⊗ q
Integration (explicit Euler, dt small):
q += ½ · [0, ω] ⊗ q · dt
q = normalize(q) <-- IMPORTANT: renormalise every step
function integrateRigidBody(body, dt) {
// Update linear state
body.vel.addScaledVector(body.force, body.invMass * dt);
body.pos.addScaledVector(body.vel, dt);
// Update angular state
body.angVel.addScaledVector(body.torque, body.invInertiaWorld * dt); // simplified scalar
// Quaternion integrate
const dq = new Quaternion(0, body.angVel.x, body.angVel.y, body.angVel.z)
.multiply(body.orient).scale(0.5 * dt);
body.orient.add(dq).normalize();
// Clear forces
body.force.set(0,0,0);
body.torque.set(0,0,0);
}
6. Inertia Tensors Reference
All tensors are diagonal in body space (aligned with symmetry axes). R = radius, L = length, a/b/c = half-extents.
Solid Sphere (mass m, radius R)
Hollow Sphere (thin shell)
Solid Cylinder (axis along z, radius R, height H)
I_zz = mR²/2
Solid Box (half-extents a, b, c along x, y, z)
I_yy = m(a² + c²)/3
I_zz = m(a² + b²)/3
Solid Cone (radius R, height H, apex at top, base centred)
I_zz = 3mR²/10
Thin Rod (length L, axis along z)
I_zz ≈ 0
I'_xx = I_xx + m·(dy² + dz²)
I'_yy = I_yy + m·(dx² + dz²)
I'_zz = I_zz + m·(dx² + dy²)
7. Numerical Integration
| Method | Order | Energy | Use case |
|---|---|---|---|
| Explicit Euler | 1st | Gains energy | Simple demos, unstable for stiff springs |
| Semi-implicit Euler | 1st (symplectic) | Conserves (near) | Rigid bodies, physics engines default |
| Verlet | 2nd | Conserves well | Particle systems, cloth, molecular dynamics |
| Velocity Verlet | 2nd | Conserves well | Standard for MD and physics engines |
| Leapfrog | 2nd | Symplectic | N-body gravity, orbital mechanics |
| RK4 | 4th | Slightly dissipative | Accurate trajectories, small dt |
v(t+dt) = v(t) + a(t)·dt
x(t+dt) = x(t) + v(t+dt)·dt <-- uses new velocity
Velocity Verlet:
x(t+dt) = x(t) + v(t)·dt + ½·a(t)·dt²
a(t+dt) = F(x(t+dt))/m
v(t+dt) = v(t) + ½·(a(t) + a(t+dt))·dt
Leapfrog:
v(t+½dt) = v(t−½dt) + a(t)·dt
x(t+dt) = x(t) + v(t+½dt)·dt
8. Constraint Forces and Joints
Constraints restrict degrees of freedom by applying corrective impulses at each timestep. The constraint Jacobian J maps constraint error to generalised forces.
C = |x_A − x_B| − L = 0 (positional error)
Ċ = dot((x_A−x_B)/|…|, v_A − v_B) = 0 (velocity error)
Baumgarte stabilisation (adds proportional term):
λ = −(Ċ + β/dt · C) / K
β ∈ [0,1] — stabilisation: 0.1–0.3 typical
Split impulse (position correction only):
Separate velocity correction impulse (affects vel) from
positional correction impulse (directly moves position).
Avoids artificial energy injection from positional correction.
| Joint type | DOF removed | Equivalent |
|---|---|---|
| Ball-and-socket | 3 translational | Shoulder, hip |
| Hinge | 3 trans + 2 rot | Door hinge, elbow |
| Slider (prismatic) | 3 trans + 3 rot − 1 | Piston, linear actuator |
| Fixed | All 6 | Weld, glue |
| Cone (angular limit) | Limits angular range | Spine, shoulder limit |