When making games with Box2D, one natural question that arises is:
"what can we determine about the general state of a body given its velocity and collision information"?
local lv = b2.Vec2(0, 0) function b2.Body:IsMoving(treshold) treshold = treshold or b2.linearSleepTolerance self:GetLinearVelocity(lv) return lv:Length() > treshold end
function b2.Body:IsRotating(treshold) treshold = treshold or b2.angularSleepTolerance local angular = self:GetAngularVelocity() return angular < -treshold or angular > treshold end
local lv = b2.Vec2(0, 0) function b2.Body:IsMovingOnAxis(ax, ay) self:GetLinearVelocity(lv) return ax*lv.x + ay*lv.y end
local wmf = b2.WorldManifold() function b2.Body:IsPushedOnAxis(ax, ay) local edge = self:GetContactList() while edge do local contact = edge.contact if contact:IsTouching() then local mf = contact:GetManifold() if mf.pointCount > 0 then contact:GetWorldManifold(wmf) local n = wmf.normal local nx, ny = n.x, n.y local other = contact:GetFixtureA():GetBody() if other ~= self then nx, ny = -nx, -ny end if ax*nx + ay*ny > 0 then return true end end end edge = edge.next end return false endOne further refinement could be to compute the total impulse acting on the body along the given axis. This can help us determine how firmly the body is being supported.
restitution = contact:GetRestitution()Depending on the restitution, we can categorize collisions in three types:
Perfectly elastic (restitution = 1)
No kinetic energy is lost so there is no sound or damage caused to the colliding objects.
Example: perfectly elastic ball that can bounce forever
Elastic (restitution > 0 and restitution < 1)
Some kinetic energy is converted into heat, sound or causes deformation.
Example: bouncing basketball
Inelastic (restitution = 0)
A lot of kinetic energy is converted into heat, sound or causes deformation.
The colliding objects remain together after the impact.
Example: ball made of soft clay that sticks to floor when dropped
Generally, momentum and energy are always conserved when dealing with a closed system. With Box2D, this is not particularly true for example when using "static" bodies with 0 restitution. Also note that, simulating deformation is beyond the scope of both Box2D and this tutorial.
As a general reference, let's look at the restitution coefficients of different types of balls:
0.858 golf ball
0.804 billiard ball
0.712 tennis ball
0.658 glass marble
0.597 steel ball bearing
velocity1 = firstBody:GetLinearVelocity() velocity2 = secondBody:GetLinearVelocity() velocityDiff = velocity1 - velocity2Next, we find how fast the two bodies are moving towards each other, given the their "difference in velocity" and position.
-- direction vector direction = firstBody:GetPosition() - secondBody:GetPosition() directionNormal = direction:Normalize() -- relative speed (in Meters per second) relativeSpeed = b2.Dot(velocityDiff, directionNormal)The resulting "relative speed" is:
worldManifold = b2.WorldManifold() function listener:PreSolve(contact, oldManifold) manifold = contact:GetManifold() if manifold.pointCount > 0 then firstBody = contact:GetFixtureA():GetBody() secondBody = contact:GetFixtureB():GetBody() contact:GetWorldManifold(worldManifold) -- Campbell's method contactPoint1 = worldManifold.points velocity1 = firstBody:GetLinearVelocityFromWorldPoint(contactPoint1) velocity2 = secondBody:GetLinearVelocityFromWorldPoint(contactPoint1) -- velocity difference vector velocityDiff = velocity1 - velocity2 -- impact speed (in Meters per second) impactSpeed = b2.Dot(velocityDiff, worldManifold.normal)When used in the "PreSolve" callback, the result is the relative speed of the contact points at the moment of impact! When we know the "impact speed" it's possible to estimate the impulse which will later be applied to the body during the "PostSolve" callback.
-- impulse restitution = 1 + contact:GetRestitution() mass = secondBody:GetMass() -- estimated impulse impulse = impactSpeed*mass*restitution -- optional: accumulate impulse impulse = impulse + oldManifold.point.normalImpulseAgain, remember that "impact speed" is actually the relative velocity of the points in contact. When torque is involved, it could be different than the linear velocity of the body.
worldManifold = b2.WorldManifold() function listener:PostSolve(contact, contactImpulse) manifold = contact:GetManifold() if manifold.pointCount > 0 then firstBody = contact:GetFixtureA():GetBody() secondBody = contact:GetFixtureB():GetBody() contact:GetWorldManifold(worldManifold) -- impulse impulse = worldManifold.normalImpulses -- optional: accumulate impulse impulse = impulse - manifold.points.normalImpulse -- impact speed mass = secondBody:GetMass() restitution = 1 + contact:GetRestitution() -- estimated impact speed impactSpeed = impulse/mass/restitutionBasically, we divide the impulse normal by the mass (changeInVelocity = impulse/mass) and de-integrate the restitution. Note that it's a good idea to check in case the mass is zero (usually for static bodies) and avoid the division altogether. That's one way to find what the "impact speed" was (past tense) at the point of contact.
Anatomy of a collision by Chris Campbell
Collision types by Joy Wagon
Coefficient of restitution by Wikipedia
The Physics Factbook by Glen Elert