2D Physics
2D physics refers to the simulation of physical behavior and interactions in two-dimensional spaces. These behaviours can include movement, collision, and gravity. This is achieved using a physics engine.
Concepts
- Collision Detection: Identifying when two objects have collided.
- Collision Resolution: Separating collided objects and preventing interpenetration.
- Broadphase and Narrowphase: Techniques to optimize collision detection.
- Impulses: Sudden changes in velocity applied during collision resolution.
- Kinematic, Static, and Rigid Bodies: Types of physical objects with different
properties and behaviors.
- Kinematic Bodies: Objects with a predetermined motion or trajectory. The body's position, velocity, and acceleration are pre-defined by the programmer, rather than determined by the physics engine.
- Static Bodies: Objects that do not move or change position.
- Rigid Bodies: Objects with mass and velocity that can move and rotate in response to physical forces like gravity and collisions but cannot deform.
- Forces: Influencing the motion of objects.
- Impulse: Instantaneous change in velocity
- Friction: Resistance to movement between two objects.
- Gravity: Attractive force between objects with mass.
- Restitution: The elasticity or bounciness of an object
Further concepts:
- Collision pairs
- Contacts, Manifolds
- Convex/Concave polygons, shapes
- Collision groups and categories
- Impulses
- Impact solver
- Contact
- Joints
Impulse Resolution
Impulse resolution is a type of collision resolution strategy. It use impulses to separate colliding objects based on the mass, position, and velocity of those objects.
We want large objects colliding with smaller ones to move a little bit during collision, and to send the small objects flying away. We also want objects with infinite mass to not move at all.
The following factors are provided from the collision detection in the form of manifolds:
- Collision normal: Direction in which the impulse will be applied
- Penetration depth: Determines how large the impulse will be
This means the only value that we need to find out is the magnitude of our impulse.
void collision_resolve(object_t A, object_t B ) { vec2 rel_velocity = B.velocity - A.velocity; float velAlongNormal = DotProduct(rv, normal); // calculate rel. velocity in terms of normal direction if(velAlongNormal > 0) { return; } // don't resolve if velocities are separating float e = min(A.restitution, B.restitution); // Calculate impulse scalar float j = -(1 + e) * velAlongNormal; j /= 1 / A.mass + 1 / B.mass; // NOTE: possible speed-up: store inverse mass // Apply impulse vec2 impulse = j * normal; A.velocity -= 1 / A.mass * impulse; B.velocity += 1 / B.mass * impulse; }
Further things to consider: Positional correction using Linear projection to avoid sinking of objects.
Simple Manifold Generation
A manifold (in physics engines) is referring to a piece of data that contains information about a collision between two objects. These manifolds are created by the collision detection phase.
struct Manifold { Object *A; Object *B; float penetration; Vec2 normal; };
bool Circle_vs_Circle(Manifold *m) { // Setup a couple pointers to each object Object *A = m->A; Object *B = m->B; vec2 n = B->pos - A->pos; float r = A->radius + B->radius; r *= r; if(n.LengthSquared( ) > r) { return false } /* Circles have collided, now compute manifold */ float d = n.Length(); // perform actual sqrt if(d != 0) // If distance between circles is not zero { m->penetration = r - d; // Distance is difference between radius and distance // Utilize our d since we performed sqrt on it already within Length() // Points from A to B, and is a unit vector c->normal = t / d; return true } else /* Circles are on same position */ { // Choose random (but consistent) values c->penetration = A->radius c->normal = Vec( 1, 0 ) return true } }
bool AABB_vs_AABB(Manifold *m) { // Setup a couple pointers to each object Object *A = m->A Object *B = m->B // Vector from A to B Vec2 n = B->pos - A->pos AABB abox = A->aabb AABB bbox = B->aabb // Calculate half extents along x axis for each object float a_extent = (abox.max.x - abox.min.x) / 2 float b_extent = (bbox.max.x - bbox.min.x) / 2 // Calculate overlap on x axis float x_overlap = a_extent + b_extent - abs( n.x ) // SAT test on x axis if(x_overlap > 0) { // Calculate half extents along x axis for each object float a_extent = (abox.max.y - abox.min.y) / 2 float b_extent = (bbox.max.y - bbox.min.y) / 2 // Calculate overlap on y axis float y_overlap = a_extent + b_extent - abs( n.y ) // SAT test on y axis if(y_overlap > 0) { if(x_overlap > y_overlap) // Find out which axis is axis of least penetration { // Point towards B knowing that n points from A to B if(n.x < 0) m->normal = Vec2( -1, 0 ) else m->normal = Vec2( 0, 0 ) m->penetration = x_overlap return true } else { // Point toward B knowing that n points from A to B if(n.y < 0) m->normal = Vec2( 0, -1 ) else m->normal = Vec2( 0, 1 ) m->penetration = y_overlap return true } } } }
bool AABB_vs_Circle(Manifold* m) { // Setup a couple pointers to each object Object *A = m->A Object *B = m->B // Vector from A to B Vec2 n = B->pos - A->pos // Closest point on A to center of B Vec2 closest = n // Calculate half extents along each axis float x_extent = (A->aabb.max.x - A->aabb.min.x) / 2 float y_extent = (A->aabb.max.y - A->aabb.min.y) / 2 // Clamp point to edges of the AABB closest.x = Clamp( -x_extent, x_extent, closest.x ) closest.y = Clamp( -y_extent, y_extent, closest.y ) bool inside = false // Circle is inside the AABB, so we need to clamp the circle's center // to the closest edge if(n == closest) { inside = true // Find closest axis if(abs( n.x ) > abs( n.y )) { // Clamp to closest extent if(closest.x > 0) closest.x = x_extent else closest.x = -x_extent } else // y axis is shorter { // Clamp to closest extent if(closest.y > 0) closest.y = y_extent; else closest.y = -y_extent; } } Vec2 normal = n - closest; real d = normal.LengthSquared(); real r = B->radius; // Early out of the radius is shorter than distance to closest point and // Circle not inside the AABB if(d > r * r && !inside) { return false; } d = sqrt(d); // Avoided sqrt until needed // Collision normal needs to be flipped to point outside if circle was // inside the AABB if(inside) { m->normal = -n m->penetration = r - d } else { m->normal = n m->penetration = r - d } return true }