Game physics covers the simulation of physical systems to create believable interactions.
For engine implementation see Unity, Unreal Engine, Godot. For game design implications see Game Designgame Feel.
Physics Fundamentals
Newton’s Laws in Games
Law
Statement
Game Application
1st — Inertia
Object stays at rest or in motion unless acted upon
Objects don’t stop unless friction/force applied
2nd — F = ma
Force = mass × acceleration
Heavier objects need more force to move
3rd — Action-Reaction
Every action has equal opposite reaction
Explosions push shooter backward
Key Quantities
Quantity
Symbol
Unit
Description
Position
x
m
Location in space
Velocity
v
m/s
Rate of position change
Acceleration
a
m/s²
Rate of velocity change
Force
F
N (kg·m/s²)
Cause of acceleration
Mass
m
kg
Resistance to acceleration
Momentum
p = mv
kg·m/s
Mass × velocity
Impulse
J = F·Δt
N·s
Instantaneous force
Torque
τ = r×F
N·m
Rotational force
Angular velocity
ω
rad/s
Rate of rotation
Moment of inertia
I
kg·m²
Rotational mass
Integration Methods
graph TD
Euler[\"Euler (explicit)\\npos += vel * dt\\nvel += acc * dt\\nFast but unstable\"]
SemiEuler[\"Semi-implicit Euler\\nvel += acc * dt\\npos += vel * dt\\nStable enough for games\"]
Verlet[\"Verlet\\npos_new = 2*pos - pos_old + acc * dt²\\nGreat for constraints\"]
RK4[\"Runge-Kutta 4\\n4 evaluations per step\\nMost accurate\"]
Integration methods comparison
# Euler (explicit) — simple but can explodedef euler(pos, vel, acc, dt): pos_new = pos + vel * dt vel_new = vel + acc * dt return pos_new, vel_new# Semi-implicit Euler — velocity updated first# MOST COMMON in games — stable for most scenariosdef semi_euler(pos, vel, acc, dt): vel_new = vel + acc * dt # update velocity first pos_new = pos + vel_new * dt # use new velocity return pos_new, vel_new# Verlet — great for constraints and particlesdef verlet(pos, pos_old, acc, dt): pos_new = 2 * pos - pos_old + acc * dt * dt vel = (pos_new - pos_old) / (2 * dt) return pos_new, vel# RK4 — most accurate, expensivedef rk4(pos, vel, acc_func, dt): k1v = acc_func(pos, vel) k1p = vel k2v = acc_func(pos + k1p*dt/2, vel + k1v*dt/2) k2p = vel + k1v*dt/2 k3v = acc_func(pos + k2p*dt/2, vel + k2v*dt/2) k3p = vel + k2v*dt/2 k4v = acc_func(pos + k3p*dt, vel + k3v*dt) k4p = vel + k3v*dt vel_new = vel + (k1v + 2*k2v + 2*k3v + k4v) * dt / 6 pos_new = pos + (k1p + 2*k2p + 2*k3p + k4p) * dt / 6 return pos_new, vel_new
Rigid Body Dynamics
Rigid Body State
Rigid body data structure
public class RigidBody{ // Linear state public Vector3 position; public Vector3 velocity; public Vector3 force; // accumulated force this frame public float mass; public float inverseMass; // 1/mass — 0 for static objects // Angular state public Quaternion orientation; public Vector3 angularVelocity; public Vector3 torque; public Matrix3x3 inertiaTensor; public Matrix3x3 inverseInertiaTensor; // Material properties public float restitution; // bounciness (0=no bounce, 1=perfect bounce) public float friction; // surface friction coefficient // Integrate physics for one timestep public void Integrate(float dt) { if (inverseMass == 0) return; // static object // Linear Vector3 acceleration = force * inverseMass; velocity += acceleration * dt; position += velocity * dt; // Angular Vector3 angularAcceleration = inverseInertiaTensor * torque; angularVelocity += angularAcceleration * dt; orientation *= Quaternion.Euler(angularVelocity * dt * Mathf.Rad2Deg); // Clear accumulated forces force = Vector3.zero; torque = Vector3.zero; } public void AddForce(Vector3 f) => force += f; public void AddForceAtPoint(Vector3 f, Vector3 point) { force += f; torque += Vector3.Cross(point - position, f); }}
Fastest collision test — just compare min/max coordinates.
AABB collision
public struct AABB{ public Vector3 min, max; public bool Overlaps(AABB other) => min.x <= other.max.x && max.x >= other.min.x && min.y <= other.max.y && max.y >= other.min.y && min.z <= other.max.z && max.z >= other.min.z; public Vector3 Center => (min + max) * 0.5f; public Vector3 Extents => (max - min) * 0.5f; // Expand AABB to contain a point public void Encapsulate(Vector3 point) { min = Vector3.Min(min, point); max = Vector3.Max(max, point); }}
SAT — Separating Axis Theorem
Convex Shape Collision convex shapes. If you can find a separating axis (where projections don't overlap) → no collision.
SAT works for ANY two
SAT — 2D polygon collision
public static bool SATCollision(Vector2[] polyA, Vector2[] polyB, out Vector2 mtv){ mtv = Vector2.zero; float minOverlap = float.MaxValue; Vector2 smallestAxis = Vector2.zero; // Test all edges of A and B as potential separating axes var axes = GetAxes(polyA).Concat(GetAxes(polyB)); foreach (var axis in axes) { var (minA, maxA) = Project(polyA, axis); var (minB, maxB) = Project(polyB, axis); float overlap = Mathf.Min(maxA, maxB) - Mathf.Max(minA, minB); if (overlap < 0) return false; // Separating axis found — no collision if (overlap < minOverlap) { minOverlap = overlap; smallestAxis = axis; } } // Minimum translation vector to resolve collision mtv = smallestAxis * minOverlap; return true;}private static IEnumerable<Vector2> GetAxes(Vector2[] poly){ for (int i = 0; i < poly.Length; i++) { var edge = poly[(i+1) % poly.Length] - poly[i]; yield return new Vector2(-edge.y, edge.x).normalized; // perpendicular }}private static (float min, float max) Project(Vector2[] poly, Vector2 axis){ float min = float.MaxValue, max = float.MinValue; foreach (var v in poly) { float d = Vector2.Dot(v, axis); min = Mathf.Min(min, d); max = Mathf.Max(max, d); } return (min, max);}
GJK Algorithm (3D Convex Shapes)
GJK (Gilbert–Johnson–Keerthi) determines if two convex shapes intersect using Minkowski difference + simplex.
graph TD
Pick[\"Pick arbitrary direction d\"]
Support[\"Find support point = furthest in direction d\\nin Minkowski difference A⊖B\"]
Simplex[\"Add to simplex (point/line/triangle/tetrahedron)\"]
Origin[\"Does simplex contain the origin?\"]
Collision[\"YES → COLLISION!\"]
New[\"Update d toward origin\\nContinue\"]
No[\"Direction away from origin found\\nNO COLLISION\"]
Pick --> Support --> Simplex --> Origin
Origin --> |Yes| Collision
Origin --> |No| New --> Support
Origin --> |Diverged| No
GJK + EPA EPA (Expanding Polytope Algorithm) computes the penetration depth and MTV (minimum translation vector) for resolution.