Custom Collision Detection

Hello,

So I’m using this code to apply customer collision detection to my game. This code only detects if two objects have collided. My problem is I need the game to know when the two objects are no longer colliding. Example: Obj1 walks off a platform(obj2) and since they would no longer be colliding obj1 would fall. Any and all help is appreciated!

 function rectCollisionTrue(obj1, obj2) if obj1 == nil then return false end if obj2 == nil then return false end L = obj1.contentBounds.xMin \<= obj2.contentBounds.xMin and obj1.contentBounds.xMax \>= obj2.contentBounds.xMin R = obj1.contentBounds.xMin \>= obj2.contentBounds.xMin and obj1.contentBounds.xMin \<= obj2.contentBounds.xMax U = obj1.contentBounds.yMin \<= obj2.contentBounds.yMin and obj1.contentBounds.yMax \>= obj2.contentBounds.yMin D = obj1.contentBounds.yMin \>= obj2.contentBounds.yMin and obj1.contentBounds.yMin \<= obj2.contentBounds.yMax return (L or R) and (U or D) end function objCollisionControl() if rectCollisionTrue(obj1, obj2) then end end Runtime:addEventListener("enterFrame", objCollisionControl)

You need to look for the phase=“ended” event which will tell you when the collision has stopped.

Isn’t the “Phase==Ended” only for the built in physics engine?

Nope…

local function thisThat(event) if event.phase == "ended" then print("bobby") end end

phase ended can be in any function your like.

You’ll probably want to keep some sort of “is touching” state around. Assuming you allow multiple objects and arbitrary collisions, you might also want some sort of compound keys. Then when the state actually changes, dispatch some event you’re watching. Something like:

local IsTouching = {} local function ComboKey (obj1, obj2) local key1 = tostring(obj1) local key2 = tostring(obj2) if key2 \< key1 then -- saves some effort down the road key1, key2 = key2, key1 end return key1 .. "::" .. key2 -- some separator in the middle -- Alternative: if there's some sort of hard limit to objects, give everybody an ID, sort them -- as we do here, then compute: id1 \* HARD\_LIMIT + id2 end function objCollisionControl() local key = Combo(obj1, obj2) local touching = IsTouching[key] local colliding = rectCollisionTrue(obj1, obj2) if not touching == colliding then -- did the state change? (best to "not" the touching, in case -- it's nil, and coerce it to false; colliding will always be -- a boolean, the way you wrote rectCollisionTrue) local event = { obj1 = obj1, obj2 = obj2 } if touching then event.name = "began" else event.name = "ended" end Runtime:dispatchEvent(event) IsTouching[key] = colliding -- switch state! end end

Untested!  :) (But the general idea is there.)

Actually you can’t use event.phase == “ended” in any function. You can only use it if the event table passed to that function has a method property.  Generally this is a) touch events and b ) collision events with Physics.

Since this collision detection method is for non physics and the detection is not being used as part of a touch event in this case.  @StarCrunch’s method would be a good way to determine if the collision was still happening.

Rob

Well i guess that is what i was getting at @Rob 

@StarCrunch I’m quite inexperienced with programming… I don’t fully understand the method you have suggested. Would you be willing to give me a fuller explanation of it? :slight_smile:

Well, generally, if you’re updating every frame, you don’t care if two objects are still touching / not touching. You only care if the state changes, touching -> not touching or vice versa. To determine that, you need to remember what the previous state was for comparison. (Box2D will have to do this somehow, too.)

At first, of course, nothing is touching anything else.

The combo key is just a way of indexing both things together. Something that can be reliably reproduced just from the items. The swap just avoids having to waste time checking two different possible keys (depending on which object was first and which second).

If touching and colliding are both true, nothing has changed; ditto if they’re both false. Thus the “if not touching == colliding then” test. (That has the side benefit of turning any nil -valued touching to true , so you’re safely comparing two booleans and it’s fine that IsTouching starts out empty.)

The event dispatch tells anybody who’s interested that “began” or “ended” happened. I sent it through Runtime, but you could send it via each object or whatever. (Or through a series of tubes.) Also, less ambiguous event names would be better, I suppose.  :slight_smile:

An alternative to the combo key / IsTouching set, and possibly more intuitive, is to keep a per-object touching set. Then you might have something like:

function objCollisionControl() local touching = obj1.m\_touching[obj2] assert(touching == obj2.m\_touching[obj1]) -- if this isn't true something's broken! local colliding = rectCollisionTrue(obj1, obj2) if not touching == colliding then -- did the state change? local event = { obj1 = obj1, obj2 = obj2 } if touching then event.name = "began" obj1.m\_touching[obj2] = true obj2.m\_touching[obj1] = true else event.name = "ended" obj1.m\_touching[obj2] = nil obj2.m\_touching[obj1] = nil end Runtime:dispatchEvent(event) -- As the notes say, write this however you like end end -- stuff local function RemoveTouchedObjects (event) local object = event.target for partner in pairs(object.m\_touching) do -- look at other objects we touched partner.m\_touching[object] = nil -- remove ourselves from other object's list end object.m\_touching = nil -- release our references end function NewObject () -- your object creation code... object:addEventListener("finalize", RemoveTouchedObjects) -- clean up after ourselves if removed object.m\_touching = {} -- keep track of touched objects return object end

The “finalize” stuff is just an attempt to handle objects getting removed mid-touch. There should be similar code in the previous example, but would actually be a chore to write.  :stuck_out_tongue:

So this is similar to the previous approach, but you do a little more preparation when you create your objects, and the objects only need to know about their personal space. :) It’s a bit redundant in that the same data is maintained twice, but that’s not a big deal right now.

You need to look for the phase=“ended” event which will tell you when the collision has stopped.

Isn’t the “Phase==Ended” only for the built in physics engine?

Nope…

local function thisThat(event) if event.phase == "ended" then print("bobby") end end

phase ended can be in any function your like.

You’ll probably want to keep some sort of “is touching” state around. Assuming you allow multiple objects and arbitrary collisions, you might also want some sort of compound keys. Then when the state actually changes, dispatch some event you’re watching. Something like:

local IsTouching = {} local function ComboKey (obj1, obj2) local key1 = tostring(obj1) local key2 = tostring(obj2) if key2 \< key1 then -- saves some effort down the road key1, key2 = key2, key1 end return key1 .. "::" .. key2 -- some separator in the middle -- Alternative: if there's some sort of hard limit to objects, give everybody an ID, sort them -- as we do here, then compute: id1 \* HARD\_LIMIT + id2 end function objCollisionControl() local key = Combo(obj1, obj2) local touching = IsTouching[key] local colliding = rectCollisionTrue(obj1, obj2) if not touching == colliding then -- did the state change? (best to "not" the touching, in case -- it's nil, and coerce it to false; colliding will always be -- a boolean, the way you wrote rectCollisionTrue) local event = { obj1 = obj1, obj2 = obj2 } if touching then event.name = "began" else event.name = "ended" end Runtime:dispatchEvent(event) IsTouching[key] = colliding -- switch state! end end

Untested!  :) (But the general idea is there.)

Actually you can’t use event.phase == “ended” in any function. You can only use it if the event table passed to that function has a method property.  Generally this is a) touch events and b ) collision events with Physics.

Since this collision detection method is for non physics and the detection is not being used as part of a touch event in this case.  @StarCrunch’s method would be a good way to determine if the collision was still happening.

Rob

Well i guess that is what i was getting at @Rob 

@StarCrunch I’m quite inexperienced with programming… I don’t fully understand the method you have suggested. Would you be willing to give me a fuller explanation of it? :slight_smile:

Well, generally, if you’re updating every frame, you don’t care if two objects are still touching / not touching. You only care if the state changes, touching -> not touching or vice versa. To determine that, you need to remember what the previous state was for comparison. (Box2D will have to do this somehow, too.)

At first, of course, nothing is touching anything else.

The combo key is just a way of indexing both things together. Something that can be reliably reproduced just from the items. The swap just avoids having to waste time checking two different possible keys (depending on which object was first and which second).

If touching and colliding are both true, nothing has changed; ditto if they’re both false. Thus the “if not touching == colliding then” test. (That has the side benefit of turning any nil -valued touching to true , so you’re safely comparing two booleans and it’s fine that IsTouching starts out empty.)

The event dispatch tells anybody who’s interested that “began” or “ended” happened. I sent it through Runtime, but you could send it via each object or whatever. (Or through a series of tubes.) Also, less ambiguous event names would be better, I suppose.  :slight_smile:

An alternative to the combo key / IsTouching set, and possibly more intuitive, is to keep a per-object touching set. Then you might have something like:

function objCollisionControl() local touching = obj1.m\_touching[obj2] assert(touching == obj2.m\_touching[obj1]) -- if this isn't true something's broken! local colliding = rectCollisionTrue(obj1, obj2) if not touching == colliding then -- did the state change? local event = { obj1 = obj1, obj2 = obj2 } if touching then event.name = "began" obj1.m\_touching[obj2] = true obj2.m\_touching[obj1] = true else event.name = "ended" obj1.m\_touching[obj2] = nil obj2.m\_touching[obj1] = nil end Runtime:dispatchEvent(event) -- As the notes say, write this however you like end end -- stuff local function RemoveTouchedObjects (event) local object = event.target for partner in pairs(object.m\_touching) do -- look at other objects we touched partner.m\_touching[object] = nil -- remove ourselves from other object's list end object.m\_touching = nil -- release our references end function NewObject () -- your object creation code... object:addEventListener("finalize", RemoveTouchedObjects) -- clean up after ourselves if removed object.m\_touching = {} -- keep track of touched objects return object end

The “finalize” stuff is just an attempt to handle objects getting removed mid-touch. There should be similar code in the previous example, but would actually be a chore to write.  :stuck_out_tongue:

So this is similar to the previous approach, but you do a little more preparation when you create your objects, and the objects only need to know about their personal space. :) It’s a bit redundant in that the same data is maintained twice, but that’s not a big deal right now.