The basic set of features that the Box2D library offers might be sufficient for simple games. However, sometimes you need to know when objects collide. For instance, in action games you usually shoot things and need to know what object was hit by your projectile.
Fortunately, the Box2D library offers an interface for collision detection in the form of callbacks. Using callbacks in the Lua language might not be straightforward because of the Lua language design. LuaBox2D contains a simple interface to set up callback functions for collision detection.
This recipe shows you how to define your own callback function in the Lua environment and how to process events when collision occurs.
First of all, you'll need a World object, some static walls, and at least one dynamic object. You can use the createWall
and createBullet
functions from the Setting up bullets recipe.
Object collision can be detected with the ContactListener
object. It provides a simple interface to a set of callback functions that are called in various stages of collision. To be precise, there are four callback functions you can override: beginContact
, endContact
, preSolve
, and postSolve
.
First, you'll have to create the ContactListener
object. After this step, you can set one of these callback functions to point to your function in the Lua environment.
The last step consists of setting a current contact listener in the World object to your new ContactListener
object. The following sample code sets a new contact listener with callback functions:
local contact_listener = box2d.ContactListener() contact_listener.beginContact = function(contact) end contact_listener.endContact = function(contact) end contact_listener.preSolve = function(contact, oldManifold) end contact_listener.postSolve = function(contact, contactImpulse) end world.contactListener = contactListener
The Box2D library allows you to observe and analyze various stages of the object collision test. An important thing to remember is that callback functions are called during the time step. All the objects are in a locked state during this stage and you shouldn't change the objects' properties. This would lead to computational errors and instability. Now, because all the callback functions are defined in the Lua environment, it's fairly easy to postpone such changes on objects until the current time step is complete.
This function is called before fixtures begin to touch. Although this function gives you only one parameter in the form of a Contact
object, it offers you everything you'll need.
The Contact object consists of many properties, but most notable among them are manifold
, isTouching
, enabled
, fixtureA
, fixtureB
, and next
.
The manifold contains a list of contact points between two fixtures. A property isTouching
tells you if these two fixtures are actually touching. You can disable further collision testing for these two fixtures by setting the enabled
property to false. Both fixtures are available from the fixtureA
and fixtureB
properties. You can access respective body objects for each of these two fixtures, and it's often used in conjunction with user data to get information about which game objects have collided.
Don't forget that there might be more than one contact. You can get the next Contact
object with the next
property.
This function is used when two objects cease to collide. Similarly, as with the beginContact
callback function, you've got only one parameter—the Contact
object.
The Box2D library calls this function whenever contacts are updated. This gives you a chance to inspect a contact just before it's used in the solver. This function gives you two parameters: the Contact
object and the oldManifold
object. This way, you detect changes in contact points.
This function lets you inspect resulting contacts after the solver has finished its job. This is particularly useful if you want to inspect contact impulses. Contact impulses can help you determine the outcome of the collision response after the solver has processed all the contacts.
The PostSolve
callback accepts two parameters—the Contact
object and the ContactImpulse
object, which contains a list of contact impulses.
Do note that Box2D might call the preSolve
and postSolve
callbacks many times even when there's only one collision. This is a result of approximation in the solver.
The following sample code shows how to use ContactListener
with user data to know exactly which game objects did collide:
local wall = createWall(Vec2(0,0), Vec2(5,1)) local bullet = createBullet(Vec2(0, 5), 1) wall.userData = { name = 'Wall' } bullet.userData = { name = 'Bullet' } local contact_listener = box2d.ContactListener() contact_listener.beginContact = function(contact) local bodyA = contact.fixtureA.body local bodyB = contact.fixtureB.body local dataA = bodyA.userData local dataB = bodyB.userData if type(dataA)=="table" and type(dataB)=="table" then print(dataA.name, 'has collided with', dataB.name) end end
As you can see, the userData
property can refer to any Lua value. This value is bound to the Body object and if you destroy the Body object, you'll lose the reference to the userData
value as well. Therefore, it's always better to store the object data somewhere in your Lua environment and use userData
only to store references in it.
You can use userData
on the Fixture
object as well to store fixture-specific information.
3.135.213.212