Collision detection without physics.

Posted by & filed under Tech Geek.

A frequent question that comes up in the Corona SDK forums is how to detect when two images on a screen hit each other. This is known as a “Collision”. The ability to determine when two things have bumped into each other is called “Collision Detection”.

Within Corona SDK, being that its an event driven system, we like the idea that we can have the system tell us when things hit each other and we just have to write the code to handle the interaction of the two. This is a cool concept and it works, but . . .

It requires using the Physics engine to do so.

Corona SDK uses a wonderful 2D Box model for physics and that model supports this event driven model and it works well. But if you’re game does not need physics and many apps don’t, like a card game where you move a card to a stack, its kind of silly and wasteful to include the overhead of the physics engine. You don’t need all of those angular momentum floating point mathematics going on in your game.

So how do I detect collisions? I don’t see any API calls to do that?

We really can’t use an event driven model like the physics engine, but there is a very simple method (well there are two) that use other features of Corona SDK, in particular using an “enterFrame” event on the Runtime listener.

Basically we have to write the code that the physics engine is doing for us and it’s not all that complex.

In my personal model, I typically have a table/array of all of my on screen objects and then I have my player’s avatar object.

local objects = {}
for i=1, 20, do
objects[i] = spawnEnemy() -- spawnEnemy creates on display objects and returns it.

local player = display.newImageRect("avatar.png", 64, 64) -- create me.

At this point we have a table of enemies in the “objects” table and my player object.

For the Runtime enterFrame event, you need a function to process the collision detection.

local function testCollisions()
for i=1, #objects do
if hasCollided(objects[i], player) then
-- do what you need to do if they hit each other

Runtime:addEventListener("enterFrame", testCollisions)

So we simply iterate over the list of objects and see if any have hit the player object using a function called hasCollided.

This is a function that you also have to write.

There are two main methods for doing this, one is based on rectangles overlapping each other. The other involves circle testing.

-- rectangle based
local function hasCollided(obj1, obj2)
if obj1 == nil then
return false
if obj2 == nil then
return false
local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax return (left or right) and (up or down) end -- circle based local function hasCollidedCircle(obj1, obj2) if obj1 == nil then return false end if obj2 == nil then return false end local sqrt = math.sqrt local dx = obj1.x - obj2.x; local dy = obj1.y - obj2.y; local distance = sqrt(dx*dx + dy*dy); local objectSize = (obj2.contentWidth/2) + (obj1.contentWidth/2) if distance < objectSize then return true end return false end

Depending on the shape of your player and objects, you may find that circles work better or rectangles work better. In my game Omniblaster I used both methods depending on the objects. My space ship has rounded shields and my rocks were basically round, and my enemies were basically round, so using the hasCollidedCircle made more sense to use.

When I say rounded, the images have transparency that when using rectangles would cause the objects to hit before they looked like it.

Yet, when I fired my phasers or torpedoes, those graphics fit nicely into rectangles, so using the rectangle hasCollided method was a better choice.

One gotcha that you need to be aware of is that if your processing takes too long, (longer than 1/30th of a second or 1/60th depending on frame rate) then your collision detection will fire again before you finish, so I usually change my collision handler to something like this:

local isCheckingCollisions = false
local function testCollisions
if isCheckingCollisions then
return true
isCheckingCollisions = true
for i=1, #objects do
if hasCollided(objects[i], player) then
-- do what you need to do if they hit each other
isCheckingCollisions = false
return true

This should be a lighter weight collision detection system than the overhead of the physics engine. Though you don't get filters, being able to define polygon shapes unless you roll you're own!

Happy coding!

10 Responses to “Collision detection without physics.”

  1. jstrahan

    hey rob on the circle detection if you get rid of the sqrt it will speed up the detection. to do this use
    if dx*dx + dy*dy < objectSize*objectSize then

  2. Mario Roberti

    Hey great post! I was JUST thinking about rolling my own non-physics collision stuff but your article saved me the trouble! Very nice!

  3. John K.

    Here is a small suggestion– When looping through the objects. Start at the end and iterate to the beginning instead.
    for i = #objects, 1, -1 do

    This allows you to remove objects on collision without throwing off the indices.

  4. Peter

    Rob, this is great and is the type of situation I’m looking at. However, I need to check if one of those items has collided with another of those 20 items. That potentially means 20!, which = 2.4329+e28 collisions I’d be checking for, right (please correct me if I’m wrong)? Would you only recommend this when you know you have a fairly limited amount of collisions you might be checking, or is there a way to make this work efficiently?

  5. Rob Miracle

    Well that certainly seems like a lot, but if you loop through your 20 objects to see if they are colliding with the other that’s only 20×20 checks per frame which should be manageable….

    for i = 1, 20
    for j = 1, 20
    if i ~= j then
    if hasCollided(objects[i], objects[j]) then
    -- do your biz

  6. Denizsecilir

    Hi Rob, thanx nice post.
    For my case i used obj1 center point

    local left = obj1.x = obj2.contentBounds.xMin
    local right = obj1.x >= obj2.contentBounds.xMin and obj1.x <= obj2.contentBounds.xMax
    local up = obj1.y = obj2.contentBounds.yMin
    local down = obj1.y >= obj2.contentBounds.yMin and obj1.y <= obj2.contentBounds.yMax
    return (left or right) and (up or down)

  7. Mike

    Great code snippets. My only issue is that I have a rotating rectangle, which this collision detection does not seem to work with reliably when using the contentBounds property.

  8. Rob Miracle

    When you rotate an object, its bounding box grows to contain the entire rectangle. Imagine drawing a rectangle around the outside of your rotated rectangle that contains the entire object. This is how this particular method works. In other words, it doesn’t work on rotated rectangles if you want the collision detection to take the rotated shape into consideration (instead of its bounding box), you either have to use the Physics Engine or find a way to implement the Separating Axis Theorem.

Leave a Reply

  • (will not be published)

XHTML: You can use these tags: <a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">