Use Box2d for Collision Detection with Corona SDK - Tutorial Part 3

Welcome back to the Corona SDK tutorial series. You might want to consider reading part one and part two first.

In this third part I will show you how you can utilize the physics simulation lib Box2d for collision detection. Box2d is a physics simulation package with many interesting features. We will use only one of those for our game: Collision Detection

To ensure that we have the same basis for this tutorial, please download the code we have written so far. Do not forget to rename the file to main.lua.

Coerce the Player to Stay on Screen

You might have noticed that the player can move off screen. To ensure that the player will stay within the screen bounds, add the following function just above the onTouch function:

-- Forces the object to stay within the visible screen bounds.
local function coerceOnScreen( object )
    if object.x < object.width then
        object.x = object.width
    if object.x > display.viewableContentWidth - object.width then
        object.x = display.viewableContentWidth - object.width
    if object.y < object.height then
        object.y = object.height
    if object.y > display.viewableContentHeight - object.height then
        object.y = display.viewableContentHeight - object.height

And call this function from within the touch handler onTouch:

player.y = event.y - player.y0
coerceOnScreen( player )
elseif "ended" == phase or "cancelled" == phase then

This code resets the player's position, if it has been moved out of the screen bounds.

Run the code in your simulator. You shouldn't be able to move the player off screen anymore.

Physics Simulation

Before our app can receive collision detection notifications from Corona, we need to add the Box2d library to the project. This can be achieved easily by the following Lua code, which you should paste just above the createPlayer function:

--Set up the physics world
local physics = require( "physics" )
--physics.setDrawMode( "hybrid" )

With the require( "physics" ) directive we let Corona know that we want to use the Box2d library. We start the physics simulation by calling physics.start(). The setDrawMode() function is a helper function we can use to debug the code related to the Box2d library.

Now we need to add the player to the physics simulation. Replace the createPlayer function with this code:

local function createPlayer(x, y, width, height, rotation)
    --  Player is a black square
    local p = display.newRect(x, y, width, height)
    p:setFillColor(0, 0, 0)
    p.rotation = rotation

    local playerCollisionFilter = { categoryBits = 2, maskBits = 5 }
    local playerBodyElement = { filter=playerCollisionFilter }

    p.isBullet = true
    p.objectType = "player"
    physics.addBody ( p, "dynamic", playerBodyElement )
    p.isSleepingAllowed = false

    return p

This change will add the player object to the physics simulation. The playerCollisionFilter defines which objects the player will collide with. We will add these objects below. The isBullet and isSleepingAllowed properties determine how the physics body will be treated in the simulation. You can read the physics engine documentation to learn more.

The objectType property will be used by us later. Do not worry about it yet.

Run the code in the simulator and watch what happens. What the heck? The player falls off the screen, although we have not written any new animation code. This is because we added the player to the physics simulation. For each object in the simulation a mass property gets calculated. And the default gravity vector is set to point to the bottom of the screen by default. The physics simulation will automatically update the position of the objects under simulation. Therefore, the player is dropping like real world objects would do. To fix this we will change the gravity vector. Add the following two lines to the bottom of the main.lua file:

-- Overhead view, like looking at a pool table from above.
physics.setGravity( 0, 0 )

Collision Detection

So far there are no objects the player could collide with. So let us add two new objects - one green and one blue square. Our goal is to detect when the player collides with one of the squares. This will look like the following screen shot.


When the player collides with the green square we will increase the score. When the player collides with the blue square we will decrease the score. As you can imagine, detecting collisions is a non-trivial task. It is even harder if objects are rotating. But as pointed out above, Box2d will do the heavy lifting for us. Now let us see how this can be done:

local function spawn( objectType, x, y )
    local object
    local sizeXY = math.random( 20, 100 )
    local collisionFilter = { categoryBits = 4, maskBits = 2 } -- collides with player only
    local body = { filter=collisionFilter, isSensor=true }
    object = display.newRect(  x, y, sizeXY, sizeXY )
    if "food" == objectType then
        object:setFillColor ( 0, 255, 0 )
        object:setFillColor ( 0, 0, 255 )
    object.objectType = objectType
    physics.addBody ( object, body )
    object.isFixedRotation = true
    return object

local green = spawn( "food", 50, 50 )
local blue = spawn( "enemy", 50, 200 )

Just add the above code at the bottom of the main.lua file. The spawn function creates a new rectangle and positions it at the coordinates given by x and y. The size of the rect will be randomized. Again we add this object to the physics simulation with physics.addBody. We define a collisionFilter for the physics body as we have done for the player object. The important part here is the maskBits = 2. This basically says that we want to detect collisions with objects that have the categoryBits set to 2: the player. Read more on collision detection in the Corona SDK docs.

When we run the code now we will see the two rectangles. But when we move the player to one of the rectangles nothing happens. The score label is not changing. The collision is already detected by the simulation and collision events are generated. But we do not listen for these events. Nor do we have code to handle these events. Listening to collision events is similar to listening to touch events. Add these lines to the bottom of your main.lua file:

-- We want to get notified when a collision occurs
local function onCollision( event )
    local type1 = event.object1.objectType
    local type2 = event.object2.objectType
    print("collision between " .. type1 .. " and " .. type2)
    if type1 == "food" or type2 == "food" then
        score = score + 1
        score = score - 1
    scoreLabel.text = score

Runtime:addEventListener ( "collision", onCollision )

As you can see we now have a handler for collision events called onCollision. We register this handler for collision events with the addEventListener function.

You will also notice that we finally used the objectType we added to the player and the two colored rectangles. Through this we can easily see what objects have collided.


We covered all parts of a simple game: the player, objects to collect, objects to avoid, and a score label. Some parts are missing though. We need a menu to start the game and we need the fun - the game logic. This is what we will add in the next part, which will be the last part of this tutorial series.

Download the code for this Corona SDK tutorial. Try to change the color, position, and the direction of the player rotation. Play with the code.

As always, I would be glad to have you as a reader of my RSS feed or as a follower on Twitter. You also can find me on Google+.

Happy coding.

Posted on CuteMachine.

Jo's Profile ImageWritten by Jo who lives and works in Frankfurt building digital doodah. Stalk him on Twitter.

TOS • Privacy • Disclaimer