Home > Tech Geek > Collision detection without physics.

Collision detection without physics.

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.

 Lua |  copy code |? 
1
2
local objects = {}
3
for i=1, 20, do
4
    objects[i] = spawnEnemy() -- spawnEnemy creates on display objects and returns it.
5
end
6
 
7
local player = display.newImageRect("avatar.png", 64, 64) -- create me.
8

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.

 Lua |  copy code |? 
01
02
local function testCollisions()
03
    for i=1, #objects do
04
         if hasCollided(objects[i], player) then
05
             -- do what you need to do if they hit each other
06
         end
07
    end
08
end
09
 
10
Runtime:addEventListener("enterFrame", testCollisions)
11

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.

 Lua |  copy code |? 
01
02
-- rectangle based
03
local function hasCollided(obj1, obj2)
04
    if obj1 == nil then
05
        return false
06
    end
07
    if obj2 == nil then
08
        return false
09
    end
10
    local left = obj1.contentBounds.xMin <= obj2.contentBounds.xMin and obj1.contentBounds.xMax >= obj2.contentBounds.xMin
11
    local right = obj1.contentBounds.xMin >= obj2.contentBounds.xMin and obj1.contentBounds.xMin <= obj2.contentBounds.xMax
12
    local up = obj1.contentBounds.yMin <= obj2.contentBounds.yMin and obj1.contentBounds.yMax >= obj2.contentBounds.yMin
13
    local down = obj1.contentBounds.yMin >= obj2.contentBounds.yMin and obj1.contentBounds.yMin <= obj2.contentBounds.yMax
14
    return (left or right) and (up or down)
15
end
16
 
17
-- circle based
18
local function hasCollidedCircle(obj1, obj2)
19
    if obj1 == nil then
20
        return false
21
    end
22
    if obj2 == nil then
23
        return false
24
    end
25
    local sqrt = math.sqrt
26
 
27
    local dx =  obj1.x - obj2.x;
28
    local dy =  obj1.y - obj2.y;
29
 
30
    local distance = sqrt(dx*dx + dy*dy);
31
    local objectSize = (obj2.contentWidth/2) + (obj1.contentWidth/2)
32
    if distance < objectSize then
33
        return true
34
    end
35
    return false
36
end
37

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:

 Lua |  copy code |? 
01
02
local isCheckingCollisions = false
03
local function testCollisions
04
    if isCheckingCollisions then 
05
        return true 
06
    end
07
    isCheckingCollisions = true
08
    for i=1, #objects do
09
         if hasCollided(objects[i], player) then
10
             -- do what you need to do if they hit each other
11
         end
12
    end
13
    isCheckingCollisions = false
14
    return true
15
end
16

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!

  1. jstrahan
    December 14th, 2011 at 19:04 | #1

    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. December 14th, 2011 at 19:14 | #2

    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. December 15th, 2011 at 01:02 | #3

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

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

  4. December 15th, 2011 at 03:58 | #4

    Great post, i use it in my next game

  5. Peter
    March 12th, 2012 at 18:04 | #5

    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?

  6. March 12th, 2012 at 18:53 | #6

    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….

     Lua |  copy code |? 
    01
    
    
    02
    for i = 1, 20
    03
        for j = 1, 20
    04
             if i ~= j then
    05
                  if hasCollided(objects[i], objects[j]) then
    06
                      -- do your biz
    07
                  end
    08
             end
    09
        end
    10
    end 
    11
    
    

  7. Denizsecilir
    March 25th, 2012 at 08:41 | #7

    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)

  8. romin
    September 19th, 2013 at 13:10 | #8

    I was expecting a sample project to test :)

  9. Mike
    November 3rd, 2013 at 11:35 | #9

    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.

  10. November 3rd, 2013 at 12:33 | #10

    @Mike
    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. http://gamedev.tutsplus.com/tutorials/implementation/collision-detection-with-the-separating-axis-theorem/

  1. No trackbacks yet.

%d bloggers like this: