Tutorial: Non-physics collision detection

Share on Facebook0Share on Google+1Tweet about this on TwitterShare on LinkedIn0

Corona has a robust collision detection engine that can accomplish many things. It’s even event-driven, which means you can sense when collisions have began and ended. This system reports when the collision happens, which two objects are involved in the collision, and more. The only “catch” is that it’s based on Corona’s physics engine. That means every moving object needs to be a physics-based object under the control of this physics engine. While the physics engine is a lean, mean, calculating machine, it’s still extra math and processing behind that scenes that you typically don’t need if you’re not using physics for other purposes — for example, if you simply want to know if object A is occupying the same screen space as object B.

Alternate detection methods

Fortunately, there are ways to detect collisions without using physics, including:

  1. Point inside a rectangle
  2. Separating axis theorem
  3. Overlapping circles
  4. Overlapping rectangles

The “point inside a rectangle” method can be complex if the rectangles have any rotation applied to them, and the “separating axis theorem” is fairly math-intensive, so neither of these will be covered in this tutorial. However, you can read about them here if you’re curious.

NOTE: Before considering non-physics collision detection, remember that if your objects are not basic rectangles or circles, the physics engine is likely the best choice to handle your collisions. Using it, you can define complex polygon-based shapes from multiple parts that represent the actual shape of your objects better than these methods will. Essentially, the non-physics methods in this tutorial follow the “close enough” methodology. If you need precise collisions, the physics engine is a better option.

Overlapping circles

In an “Asteroids” type game, the ship — even though it might appear triangular — can probably be represented by a circle: either a circle that encloses the entire ship, or a circle that spans a slightly smaller area around the center of the ship. The asteroids flying around can also be roughly represented by circles. During fast gameplay, the player will not really notice exact shape-precise collision detection.

Some very simple calculations can tell you if any two circles of an arbitrary sizes are overlapping:

Here, you pass in two display objects. Since a circle is still a “rectangular image” from a display object standpoint, we’ll use the contentWidth to determine the size of the circles. If the two circles are closer than the distance of their centers, they are touching.

Overlapping rectangles

This code was originally located in the the Corona Labs Code Share:

This function uses the built-in contentBounds of each rectangle to see if they have overlapped. This works great for tiles and cards since they are visually rectangular. It uses a set of if statement checks to see if any corner of one rectangle is inside the bounds of the other rectangle. For graphics that have some transparency around them, note that this will include the transparent areas. Both functions will return true if the objects/points are colliding with the other object.

Checking for collisions

Now that you have a couple different ways to determine if two items have collided, how do you actually use them? Unlike physics-based collisions, these are not listener events where the system tells you exactly when they collide. Instead, you must check periodically in your own code. The two primary ways are:

  1. While touch-dragging an object around the screen.
  2. Checking each frame using an "enterFrame" Runtime listener (“game loop” method).

Touch and drag

You’ve probably seen this touch-drag code before:

Now, for collision detection, add a check inside the "moved" or "ended"/"cancelled" conditional blocks, depending on when you need to check for the collision.

For simplicity, let’s use an example of dragging a card to a “drop zone,” potentially represented in your user interface by some outline graphic.

Now, you can request collision detection inside the standard drag handler. In this case, since you only care if the object is “dropped” in the right place, use the "ended"/"cancelled" phase:

With this code, if you drag the card and drop it outside the drop zone, it will transition back to its original location. If the card touches the hot spot, then it will snap the target into place!

Let’s look at another example. This time you will spawn a bunch of coins and then touch-drag myPlayer to pick them up. Instead of doing the collision check in the "ended"/"cancelled" phase, you’ll check in the "moved" phase so that the collision report will occur immediately when myPlayer crosses over a coin:

Runtime detection (game loop)

These collision techniques also work well in "enterFrame" listeners where you can check for things that are transitioning around that the user isn’t directly in control of.

For this to work, your objects need to be in an array to loop through. Using the same coins above, let’s apply some transitions to move them to the bottom of the screen and then remove those coins that hit our player. For additional effect, if the coin hits our player, we’ll turn it blue.

This concludes the tutorial on non-physics collision detection. As you can see, this is a convenient method when you’re building an app where you require basic collision detection, but the physics engine — as powerful as it can be — is overkill for the required tasks.

Share on Facebook0Share on Google+1Tweet about this on TwitterShare on LinkedIn0
Rob Miracle

Rob Miracle creates mobile apps for his own enjoyment and the amusement of others. He serves the Corona Community in the forums, on the blog, and at local events.

This entry has 15 replies

  1. RichieLoco says:

    Excellent blog post as usual! This will come in useful

    Many thanks Rob

  2. Cleverson says:

    Great article!
    Any benchmark regarding using these “manual” techniques or leaving everything to the physics engine?

  3. Rob Miracle says:

    I don’t know of any benchmarking, but physics has to have a list of object to iterate over to check for collisions, then they have to call functions to dispatch events that then have to call function handlers and a bunch of stuff going on in floating point number space.

    It can’t be faster than iterating over a list of objects, calling a function to do some integer math (for the rectangles) and limited floating point math for the circles (and there is a version that drops the square root’s out for even more performance). So it’s should be similar in performance if not a bit faster.

    Of course there are a few things you can do to speed up this even more, like in the coins example, doing:

    local coin = coins[i]

    and then doing all the test on coin (removes a couple of table lookups) (thanks Danny!).

    • Cleverson says:

      Thanks Rob!

  4. finefin says:

    Here’s another one – Line intersection:

    — Intersecting Lines —
    — example: doLinesIntersect ( line1.pointA, line1.pointB, line2.pointA, line2.pointB )
    — points are tables containing x and y coordinates
    — returns: true, coordinate / false
    doLinesIntersect = function ( a, b, c, d )
    local L1 = {X1=a.x,Y1=a.y,X2=b.x,Y2=b.y}
    local L2 = {X1=c.x,Y1=c.y,X2=d.x,Y2=d.y}
    local d = (L2.Y2 – L2.Y1) * (L1.X2 – L1.X1) – (L2.X2 – L2.X1) * (L1.Y2 – L1.Y1)
    if (d == 0) then
    return false
    local n_a = (L2.X2 – L2.X1) * (L1.Y1 – L2.Y1) – (L2.Y2 – L2.Y1) * (L1.X1 – L2.X1)
    local n_b = (L1.X2 – L1.X1) * (L1.Y1 – L2.Y1) – (L1.Y2 – L1.Y1) * (L1.X1 – L2.X1)
    local ua = n_a / d
    local ub = n_b / d
    if (ua >= 0 and ua = 0 and ub <= 1) then
    local x = L1.X1 + (ua * (L1.X2 – L1.X1))
    local y = L1.Y1 + (ua * (L1.Y2 – L1.Y1))
    return true, {x=x, y=y}
    return false

  5. Dpeif says:

    Great post, very informative and applicable to many projects. Thanks for posting, very well written too.

    Just as a general question, are there advantages to using these methods for UI functionality, i.e. the card drag-drop-snapback scenario, instead of using jQuery-UI’s .draggable() or .droppable() ?

  6. Rob Miracle says:

    Well, given that Corona SDK Native apps are built in Lua and not Javascript, you can’t use jQuery and jQuery UI, so yea, there is a big advantage. Sure you can have native.newWebView’s which can run web pages using jQuery et. al. but if you’re going to use Corona SDK you’re not going to be interfacing with jQuery.

  7. Ariel says:

    We have started using HardonCollider which is a very neat lua collision library implementing the GJK algorithm.
    I don’t have any performance number comparing to Box2D but for our 2D shooter it works much smoother.
    It is much lighter and provides only part of the collision functionality from Box2D as it doesn’t supply you with the collision point. It doesn’t even resolve the collision but it does provide you with a displacement vector with which you can resolve the collision and also compute the aproximate collision point.

    It also supports Points, Circles, Rectangles and any other convex or concave (which Box2D doesn’t support) polygons.
    You also have some cool API calls for hit testing for collisions within a region without having a real body colliding. This is useful for example when our character punchs and we want to see which enemies are in the punch area.

    It does require a little bit of code tweaking to get it going because it was designed to be used with the LOVE framework so you’ll need to modify two things:
    1. In the first line of the init.lua file you need to change it to:
    local _NAME, common_local = string.gsub( …, “.init”, “”), common
    And when you load the module you need to explicitly require the init.lua file and not like in the tutorial, that’s because package.path is configured differently in LOVE and it automatically searches for a init.lua file when you require a folder.

    2. If you want debug polygon shape printing you’ll need to replace the love.draw calls with Corona draw functions.

    Took us about an hour to hook it in our game and we are very happy with it.

  8. Vonn says:

    In the rectangle part, what If my object has a mask on it?

    • Rob Miracle says:

      It uses the bounding box of the image. Masking should not matter.


      • Vonn says:

        It seems that even if lets just say an image with mask of a circle is the coin, and I have a circular object to(player). even if I don’t hover over the circle. it still detects the unmasked area

        • Rob Miracle says:

          Even an image of a coin is a rectangle. The techniques above are shape agnostic. They don’t know what the image is. The don’t even know it’s an image. It’s taking the position, width and height of one “box” and checking it against the other boxes on screen. It’s all just math and seeing if the bounding boxes overlap. But if you’re doing coins, and want a circular check, there is also a function that uses the radius of a circle to determine if they touch. This might be a better option.

          While we didn’t cover it in this tutorial, there is another method that works with polygons including rotated rectangles, but you will have to search the internet looking for the Separating Axis Theorem” or something like that.

  9. Great Article! What if I have two objects that I can move around but I only want one of the objects to react when it collides with my hot spot.

    I tried adding and and to your code above and this and it wouldn’t work.

    if (hasCollided(event.target,plantPot)) and event.target.name==”flagon” then
    transition.to( obj,{time=2000, rotation=360,x=plantPot.x, y=plantPot.y})

  10. No worries, I got it all figured out. Thanks Rob, for the article again.

  11. Scott says:

    How would you use this if a rectangle is at an angle?
    floor = display.newRect( 0,0, 268, 45 )
    floor.x = 359
    floor.y = 233
    floor.rotation = 330