Chap. 08B --- Collision Handlers

Collision Handling Basics

When two objects collide, it may be desirable to run some Lua code. For example, when a ball hits a wall, you might want to play a "boing" sound. To make things happen when objects collide, you will need a collision handler.

Note: if SceneObject:setCollidable(false) is set on either object, neither the collision handler on the object with setCollidable(false) or the object it's hitting will call their collision handler. setCollidable(false) effectively drops out all collision checking from that object, so it is as if the object doesn't exist in the collision system at all. If you would like objects to pass through each other, but still call collision handlers when they do, see Pass Through below.

Collision is NOT Instantaneous

Before we tell you how collision handlers work, we need to explain what collisions actually are: two objects that have very slightly penetrated each other. As soon as the two objects interpenetrate, the physics system starts applying forces to shove the two objects apart. But it may take a little time for the forces to cause the two objects to separate. Because of this, collisions actually have three stages:

  • start: the objects have just interpenetrated each other.
  • continue: the objects have been interpenetrating for a while, and the physics system is still trying to shove them apart.
  • end: the physics system has finally succeeded in pushing the objects apart. They are no longer interpenetrating.

This is important: you need to remember that collision is not an instantaneous process - it can take quite a bit of time, in computer terms.

Collision Handlers

A collision handler is a Lua function that gets called when two objects collide. It takes three arguments: the first object (the object which has the collision handler set), the second object (the object against which it is colliding), and the stage, as described above. The stage is a string which can be "start", "continue", or "end".

The collision handler must be attached to a particular object using setCollisionHandler. Here is a simple example, which causes a ball to play a sound when it collides with something:

function boingCollisionFunction(obj1, obj2, stage)
    if (stage == "start") then
        Sound.new("alice/boing.mp3"):play()
    end
end

ball:setCollisionHandler(boingCollisionFunction)

In a typical collision, the collision handling function will get called many times: once at the start of the collision, several times during the continuation of the collision, and once at the end of the collision. But we only want the 'boing' sound to get played once, so we check the 'stage' parameter. We only play the sound at the start of the collision.

One Collision, Two Handlers

When two objects collide, if both objects have collision handlers, then both collision handlers will get called. It is not predictable what order the handlers will get called in.

Sometimes, the two collision handlers may produce twice the effect you desire. For example, consider a billiards table where each ball has a collision handler that makes a "click" sound. When two balls collide, the two handlers will get called, and you'll get two clicks --- not what you want.

In this case, the solution is to use a rule to disable one of the two clicks. A good rule in this case would be for the collision handler of the higher numbered ball to make the click-sound, and the collision handler for the lower-numbered ball to stay silent:

function billiardBallCollisionHandler(obj1, obj2, stage)
    if (stage == "start") then
        if (obj1.ballNumber > obj2.ballNumber) then
            Sound.new("alice/click.mp3"):play()
        end
    end
end

In fact, as a convenience feature, all SceneObjects can be compared against each other with <, <=, >, >=, and == operators and will return mathematically consistent answers (i.e. if objectA > objectB is true, objectB < objectA is also true). This feature can be useful in writing collision handlers similar to the one above.

Pass-Through

When two objects interpenetrate, the physics engine immediately applies forces to push them apart. If a collision handler calls the method SceneObject:passThrough, this will suppress these separation forces. Effectively, this will cause the two objects to pass through each other like ghosts.