Physics part 6

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"?

Is the body moving?

One approach is to compare the linear velocity of the body against a given threshold. This method returns true for a body resting on top of a moving platform.
local lv = b2.Vec2(0, 0)
function b2.Body:IsMoving(treshold)
  treshold = treshold or b2.linearSleepTolerance
  self:GetLinearVelocity(lv)
  return lv:Length() > treshold
end

Is the body rotating?

By looking at the angular velocity of a body we can determine if it's spinning. Note that the angular velocity could be negative. This approach would always return false for bodies with "fixed rotation".
function b2.Body:IsRotating(treshold)
  treshold = treshold or b2.angularSleepTolerance
  local angular = self:GetAngularVelocity()
  return angular < -treshold or angular > treshold
end

Is the body moving along an axis?

This is a common question, when you want to find out if an object is "falling". We can use the dot product to check for movement along a given axis (ax, ay). For example, you can use the gravity vector as an axis.
local lv = b2.Vec2(0, 0)
function b2.Body:IsMovingOnAxis(ax, ay)
  self:GetLinearVelocity(lv)
  return ax*lv.x + ay*lv.y
end

Is the body pushed along an axis?

Using the contact information, we can check if a body is "pushed" by other bodies in a given direction. This applies for resting contact too. Suppose that we have a rectangle supported on top of a moving platform. Using the gravity axis, we check if the platform is "pushing up" the rectangle. It's admittedly inefficient, but an accurate way to tell if a given body is "on the ground".
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
end
One 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.

Is the fixture flat on its side?

Consider again the example of a rectangle resting on top of a platform. The number of points in each contact tells us how the body is being supported. If there is one point, the rectangle is supporting itself by a single vertex. If there are two points, the rectangle is flat on its side. There cannot be more than two contact points between a pair of (convex) fixtures.

Rectangle supported by a platform. Contact points shown in white and collision normal in blue
Left: 1 contact with 1 point (vertex to edge)
Right: 1 contact with 2 points (edge to edge)


Note that it's important to consider the direction of each collision normal in order to determine if a contact is pushing the body up, down, sideways or both! One fixture can be in contact with several others, so it's possible for the poor rectangle to be "sandwiched" or pushed in opposite directions at the same time.

What is the "right" coefficient of restitution?

The restitution coefficient affects colliding bodies along the normal axis and is described as "the degree of relative kinetic energy retained after a collision". Box2D provides the following function to get the restitution between two colliding fixtures:
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

What is the relative velocity of any two bodies?

One simple approach is to subtract the linear velocities of the two bodies (assuming that their rotation or torque is insignificant).
velocity1 = firstBody:GetLinearVelocity()
velocity2 = secondBody:GetLinearVelocity()
velocityDiff = velocity1 - velocity2
Next, 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:
  • positive when one or both bodies are moving towards each other,
  • zero when both bodies are immobile or moving together in parallel,
  • negative when one or both bodies are moving away from each other.


    The relative velocity in one dimension: two cars located at two positions (p1 and p2) moving at speeds (v1 and v2).
    direction = p1 - p2
    directionNormal = direction/abs(direction)
    velocityDiff = v1 - v2
    relativeSpeed = velocityDiff*directionNormal

    What is the relative velocity at the moment of collision?

    Collisions in Box2D are resolved when the "world:Step" function is called. That's why there is the "ContactListener" class - so our game is notified of collision events that happen during "world:Step".

    The "ContactListener" class has four callbacks: "BeginContact", "EndContact", "PreSolve" and "PostSolve". Just remember that you really, really want to be using "Pre-" and "PostSolve" to buffer only the most essential collision data. Bodies and fixtures that are irrelevant to your game should be ignored altogether. Collision callbacks may be evoked several times per fixture which could easily amount to hundreds, even thousands of calls per frame.

    Based on contact points (BeginContact, PreSolve)
    The following method should work best during the "BeginContact" or "PreSolve" callbacks, before the velocity of the body is modified by Box2D. Given a contact point between two fixtures, Chris Campbell shows us a clever way to determine the "relative velocity of the contact points on each body". Campbell's approach is useful because it implicitly takes into account the bodies' angular velocity.
    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[1]
        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[1].normalImpulse
    Again, 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.

    Contact points shown in white, and their velocities shown in green.
    Left: with torque
    Right: without torque


    Based on impulse normal (PostSolve)
    During the "PostSolve" callback, the body has already reacted to the collision and Box2D is reporting to us what impulses were applied. Based on the impulse information in the "b2.ContactImpulse" object, we can backtrack and find what the relative velocity was during impact:
    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[1]
        -- optional: accumulate impulse
        impulse = impulse - manifold.points[1].normalImpulse
        -- impact speed
        mass = secondBody:GetMass()
        restitution = 1 + contact:GetRestitution()
        -- estimated impact speed
        impactSpeed = impulse/mass/restitution
    Basically, 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.

    How much energy is involved in a collision?

    This is a common problem when programming games: suppose that you want to play a sound effect when two objects collide sufficiently hard. But what does a "hard collision" really mean? Take the restitution coefficient for example. In a perfectly elastic collision (restitution = 1) all kinetic energy is preserved so (in theory) no sound would be produced at all. Before we discuss how kinetic energy may be "lost" during collisions... to be continued

    References:
    Anatomy of a collision by Chris Campbell
    Collision types by Joy Wagon
    Coefficient of restitution by Wikipedia
    The Physics Factbook by Glen Elert