Posted on by

Today’s tutorial introduces the Corona physics contact, a feature that was recently implemented within the physics engine. Generally speaking, a physics contact is a “reference to a specific collision that is about to occur.”

Developers familiar with the physics pre-collision might ask “how does a physics contact differ?”. While the physics contact will, in almost every case, be used during a pre-collision event, there are some important differences to note.

Pre-collision Event

In a typical pre-collision (or any collision) between two objects, Corona allows you to access each object by Lua ID. With those references, you might decide to perform an action on one (or both) objects. For example, you might change one object to a sensor, allowing it to pass through the other object when the collision occurs. Or you might choose to destroy one of the objects, animate it, apply a specific force upon it, or any number of other actions. This has all been possible in Corona for a long time, but there are a few limitations that the physics contact can solve.

One-Sided Platforms

A common requirement in 2D side-view games is the “one-sided platform” such as those in Super Mario Brothers and a thousand other 2D platform games. The character can jump from beneath a platform, pass through the platform, and land nicely atop it. Previously, this was “solved” in Corona by changing the character into a sensor at the beginning of a jump, such that it passes through the platform. Then, upon “exit” from the platform, the character is reverted back to a normal object.

While this is an acceptable solution in most cases, it presents at least one obvious problem. What if an enemy “goomba” is walking atop the platform? The character will still be a sensor when it collides with the enemy’s feet and will pass directly through! Similarly, what if the character collides with some other non-platform object during its upward trajectory toward the platform? Post-collision forces such as “bounce” would be lost.

Introducing the Physics Contact

The solution to the above dilemma can be solved with Corona’s new physics contact. As stated above, you can consider this as a specific reference to a collision that is about to occur. When used within a pre-collision event listener (typical usage), you can access the physics contact and four properties that weren’t previously accessible.

Let’s consider the one-sided platform example again. When the character jumps and collides with the platform from below, Corona understands that a collision is about to occur between the character’s head and the bottom of the platform. Using a physics contact, you can gain temporary access to this upcoming collision and tell Corona how you’d like to handle it. You can even tell Corona to ignore it completely!

Here’s how to access the physics contact in code, using a pre-collision event listener:

function character:preCollision( event )

   print( event.contact )  --"event.contact" is the physics contact "userdata"
   --the following properties of the collision can be accessed; the last three are settable!
   print( event.contact.isTouching )  --read-only: are the two objects touching?
   print( event.contact.isEnabled ) --'true' or 'false'; will the collision resolve?
   print( event.contact.bounce )  --get/set the bounce factor of the collision
   print( event.contact.friction )  --get/set the friction factor of the collision

end
character:addEventListener( "preCollision" )

Notice that the physics contact provides you with collision-specific properties that you cannot access in a traditional listener events. Using basic conditional logic, you can then opt to ignore a collision entirely, or change the bounce/friction factor(s) for that specific collision, overriding the inherent bounce/friction that you declared for the object(s).

For a one-sided platform, the physics contact method is much better. You initially set a property of the platform as “pass through” (or any other term), and then, using conditional logic, you tell Corona to ignore the collision entirely if the the platform is a “passthru” type, as in this example:

local platform = display.newImage( "platform.png" )
platform.collType = "passthru"
physics.addBody( platform, "static", { bounce=0.0, friction=0.3 } )

function character:preCollision( event )

   local collideObject = event.other
   if ( collideObject.collType == "passthru" ) then
      event.contact.isEnabled = false  --disable this specific collision!
   end
end

It’s important to note that the physics contact is not a reference to either object involved in the collision, but rather a reference to the collision itself. With that reference, you can opt to override the collision entirely — or just tweak a few optional properties of it — before it actually occurs. Setting the physics contact properties does not permanently set those properties on either object involved — Corona only uses the properties for that specific collision, then it discards them. For example, if you change the “bounce” factor of the physics contact, it will not set/reset the core bounce factor on either object involved.

In Corona, the physics contact (and its properties) can be accessed from any type of collision listener: pre-, post-, or standard. In most cases, you will access it from a pre-collision, because logically you need to access or set a property of a collision before it occurs, not when or after it occurs.

Other Possibilities

While the one-sided platform case is fairly common, there are other potential applications of the physics contact. For example, in a “pinball” game, you could use conditional logic (and physics contacts) to achieve the following:

  • Silver balls collide with red bumper for extreme bounce (set physics contact bounce to 1)
  • Silver balls collide with blue bumper for no bounce (set physics contact bounce to 0)
  • Gold balls collide with red bumper for no bounce (opposite of the first case)
  • Gold balls collide with blue bumper for medium bounce

See it in Action

Check out this demo project to see the “one-sided platform” method in action, using physics contacts.

In Summary…

The new physics contact provides four new properties that can be accessed on a specific collision basis:

  • event.contact.isTouching — read only: are the two objects touching?
  • event.contact.isEnabled — read/write: ‘true’ or ‘false'; should the collision resolve?
  • event.contact.bounce — read/write: get or set the bounce factor of the collision
  • event.contact.friction — read/write: get or set the friction factor of the collision

In special circumstances, these properties allow a level of fine-tuned control that was not previously possible using basic collision events and collision filters. Give them a try in your next physics-based project and, as usual, please open up the conversation below with your questions, comments, and observations.


Posted by . Thanks for reading...

14 Responses to “Tutorial: Introducing Physics “event.contact””

    • Brent Sorrentino

      Hi Matt,
      I thought that PhysicsContact would be the solution, but apparently it’s not. However, I sincerely am investigating this collision bug and I’m seeking a resolution a.s.a.p. So, please be patient a bit longer. :)

      Reply
  1. sam3dus

    This is great news and will make our testing much easier.

    But how do you use this with a spawn + table function?

    balls = { }
    function spawnBall ()
    local ball etc
    –add to balls table etc

    ball:addEventListener( “preCollision” ) — throws the expected assertion error

    end

    function ball:preCollision( event )

    Reply
    • Brent Sorrentino

      Hello,
      You can achieve this by using “local” pre-collisions. Replace the global function “char:preCollision” in the demo project with this local function (internal contents remain the same):

      local function preCollision( self, event )

      end

      And then add a local collision listener to each ball in your scenario:

      ball.preCollision = preCollision
      ball:addEventListener( “preCollision”, ball )

      That should work. And thanks for bringing this topic to my attention; certainly its a useful method if there are many objects following this preCollision behavior.

      Reply
      • sam3dus

        Thanks for the reply. Don’t why I thought it required something other than the usual approach.

        Anyhow, this:

        function onLocalPreCollision( self, event ) — can’t use local on this function

        print( “enabled ” .. event.contact.isEnabled )

        Throws this error
        Build: 2012.971
        Runtime error
        d:\coronasdk\game\main.lua:616: attempt to concatenate field ‘isEnabled’ (a boolean value)

        Also, imho, adding the four new properties to ‘self’ would easily be as useful as your adding them to ‘event’

        Reply
        • Brent Sorrentino

          Your error is a concatenation error, not an error with the event.contact. For some reason (I have never determined why), Lua occasionally gripes when you try to concatenate two items. Instead of concatenating them, just type it like:

          print( “enabled”, event.contact.isEnabled )

          Reply
          • sam3dus

            Thanks for that info.

            Everything is working great now and event.contact is making things a lot easier for us.

Leave a Reply

  • (Will Not Be Published)