Complicated issues involving collision "ended" timing

Hi, I’ve been encountering issues with trying to coordinate logic with collision “ended”. Two examples:

Let’s say I am trying to decorate a christmas tree. The tree has a complex multi-element physics body (exported from Physics Editor).

Example 1: Snow falls from the sky and onto the tree. 

Currently, I would like snow to fall vertically from the sky and land randomly somewhere on the tree’s physics body. My current method works as follows: When a snowflake/tree collision begins , I set a timer (called  land timer ) with a random time, which upon timer completion will stop the snowflake in place and remove the physics body.

But here’s the tricky part… if the snowflake is no longer touching the tree… instead of stopping the snowflake outside the tree, I want it to continue falling again (either 1. offscreen, or 2. if it starts to touch the tree again… say a lower branch… resume the timer from above). I tried doing this by canceling the  land   timer  upon collision ended. Unfortunately though, the timing hasn’t been working correctly. Sometimes snowflakes land just outside the tree’s physics body, which seems to mean that the collision ended phase is too late for me to cancel the land timer.

Example 2: Painting the tree.

Let’s say I have a big paint brush and would like to paint the tree. I’d like the paint to come from the tip of the brush, which is offset quite a bit from where I am touching. So, going with ideas inspired by this post, I tried to coordinate touch and collision events. I put a physics body on the tip of the brush , where I would like paint to come from. When the brush tip collide begins with the tree, I set a flag isColliding. When the collision ends , I set the flag to false. Then, in my brush " touch" handler, while I’m dragging the brush around, I use this flag to determine if I can draw on the tree or not (since I don’t want to paint anywhere but the tree). Unfortunately, I am getting issues where if I touch move quickly , it appears the isColliding flag is being set as false too late… in which case, it is drawing outside the tree.

In both these examples, I am guessing these issues happen because the collision ended occurs at the first recognized instance in which the physics objects are no longer touching (as opposed to the instant right before they will stop touching, or the exact position where the collision ended). Feel free to correct me if I’m wrong. My question is how can I overcome these complicated issues? Is there a way I can get the notified a bit earlier than when I am already outside the physics body?

I feel like a " contains" function would be extremely useful. For example, if for all the complex physics bodies/shapes that I export from the PhysicsEditor, I could do something like myObj:contains(x,y). This would be so much easier than trying to coordinate collisions and touches, which might not time correctly. In Example 1, I could easily just call the function to test if I should stop the snowflake or not. In Example 2, I could call the function to test if I can paint or not.

I am using Build 2014.2511. I’d really appreciate some advice as these are some complicated issues that have recurred quite a bit while trying to implement more advanced functionalities. Feel free to let me know if you have any questions. I know it’s a lot to take in. Thank you very much!

Hi @jhow,

Thanks for the extremely detailed description. Physics challenges are fun to solve (for me at least) so let’s get started. :slight_smile:

My first question is each case is: are you using a collision “counter” on the objects (snowflakes or brush tip) in regards to the multi-element tree body? Remember that when dealing with multi-element bodies, you will get a “began” and “ended” phase for each element of each body that collides. This is a crucial aspect to understand, and if you’re not keeping count of this, that’s likely why your timers and such are being thrown into whacky behavior.

This tutorial describes it in greater detail… read the “Part II” in specific:

https://coronalabs.com/blog/2013/01/08/working-with-multi-element-physics-bodies/

Best regards,

Brent

Hi @Brent Sorrentino,

Yep. That is being handled. I pretty much followed that tutorial so my began handling only starts on the “began” on the first element that’s touched and the ended handling only happens on the “ended” of the last element touched.

Also, the tree is multi-element while the physics bodies for the snowflakes and the paint brush tip are just single circles.

Jeff

Hi Jeff,

OK, yes, but are you tracking how many collisions stack up and “stack down” as the object moves across the tree?

Brent

By this you’re referring to something along the lines of your “Managing the Count” section of your link, correct?

I think I have pretty much completed the same thing with a few minor changes. My code:

local function onBrushTipCollision(self, e) -- Multi-element Abstraction local eStates = self.elementStates local treeElement = e.otherElement if e.phase == "began" then if not eStates[treeElement] then eStates[treeElement] = 0 end if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn + 1 end eStates[treeElement] = eStates[treeElement] + 1 elseif e.phase == "ended" then eStates[treeElement] = eStates[treeElement] - 1 if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn - 1 end end -- Phase Handling if self.elementsIn \> 0 and not self.isColliding then print("BrushTip Collision: " .. e.phase) self.isColliding = true elseif self.elementsIn == 0 and self.isColliding then print("BrushTip Collision: " .. e.phase) self.isColliding = false end end brushTipCircle.collision = onBrushTipCollision brushTipCircle:addEventListener("collision", brushTipCircle)

The main difference here is that I am in the touch event of the brush object counting the elements of the “otherElement” (ie. the tree). In your example you do “selfElement”. Also, the conditionals in the phase handling are slightly different to handle my specific case. Does that answer your question?

Hi Jeff,

Preferably, let’s deal with the falling snowflakes case first. Can I see the code you’re using?

Brent

Here’s a bit of the code. It’s actually the least evil of the two examples since I was actually able to hack up some code to compensate a little bit for this situation:

local function spawnSnowflakeImg(type) local snowflakeImg = display.newImage(imageSheet, sheetInfo:getFrameIndex("Snowflake" .. type)); local clr = kSnowflakeData[mRand(#clrs)] snowflakeImg:setFillColor(clr[1], clr[2], clr[3], 1) snowflakeImg.rotation = mRand(-rot,rot) return snowflakeImg end local function spawnSnowflake(x, y) local snowflakeImg = spawnSnowflakeImg(sType) decorateGrp:insert(snowflakeImg) snowflakeImg.x, snowflakeImg.y = decorateGrp:contentToLocal(x,y); local bodyName = "Snowflake" physics.addBody(snowflakeImg, shapedefs.physicsData():get(bodyName)) table.insert(thrownObjs, snowflakeImg) snowflakeImg.elementStates = {} snowflakeImg.elementsIn = 0 snowflakeImg.isColliding = false local landTimer local function cancelLandTimer() if landTimer then timer.cancel(landTimer) end landTimer = nil; end local killBodyTimer local function cancelKillBodyTimer() if killBodyTimer then timer.cancel(killBodyTimer) end killBodyTimer = nil; end local function onLocalCollision(self, e) -- Multi-element Abstraction local eStates = self.elementStates local treeElement = e.otherElement if e.phase == "began" then if not eStates[treeElement] then eStates[treeElement] = 0 end if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn + 1 end eStates[treeElement] = eStates[treeElement] + 1 elseif e.phase == "ended" then eStates[treeElement] = eStates[treeElement] - 1 if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn - 1 end end -- Phase Handling if self.elementsIn \> 0 and not self.isColliding then -- Collision Begins self.isColliding = true -- Slow down snowflake local vx, vy = snowflakeImg:getLinearVelocity() snowflakeImg:setLinearVelocity(0.5\*vx, 0.5\*vy) snowflakeImg.gravityScale = 0.85 -- Calculate a rough random time when to land the snowflake -- based upon vertical distance to bottom of tree (obtained from ray cast) local contentX, contentY = snowflakeImg:localToContent( e.x, e.y) local hits = physics.rayCast(contentX, kTable["yMax"]+100, contentX, kTable["yMin"], "closest") local hitDist if hits and hits[1] then hitDist = mAbs(hits[1].position.y - contentY) end local yDist = hitDist or 200 landTimer = timer.performWithDelay(mRand(0,4\*yDist), function(e) -- Stop snowflake in place snowflakeImg:setLinearVelocity(0, 0) snowflakeImg.gravityScale = 0 -- Currently delaying body removal due to the fact that sometimes the land timer is not cancelled in time killBodyTimer = timer.performWithDelay(600, function(e) physics.removeBody(snowflakeImg) end) end) elseif self.elementsIn == 0 and self.isColliding then -- Collision ends self.isColliding = false cancelLandTimer() cancelKillBodyTimer() -- Snowflake no longer touching tree --\> continue falling. snowflakeImg.gravityScale = 1 end end snowflakeImg.collision = onLocalCollision snowflakeImg:addEventListener("collision", snowflakeImg) end

You’ll see I delayed the body removal using a “kill body timer”. This will delay the body removal for the situations when the the snowflake stops outside the cake (ie. the land timer finishes, but the collision ended phase has not been resolved). If possible, preferably, I would like to accomplish this without the kill body timer. (ie. assuming I can be able to remove the body immediately).

Hi Jeff,

I think you may be over-complicating this a bit. :slight_smile: Try the following example that I put together.

Here are a few notes before you test it:

  1. Notice that I made the flake physics bodies (radius) smaller than the actual circle, which helps prevent the impression that the flake is just barely clinging to the bottom edge of the surface it’s on.

  2. This example doesn’t utilize pausing and resuming of the land timer, but is that really necessary? If a flake falls off the first object, and has a remaining land timer amount of (say) 115 milliseconds, is it crucial that it uses 115 milliseconds for the land timer when it falls onto the second object? My example just cancels the timer and starts a new timer (random between 200-1200).

[lua]

local physics = require( “physics” )

physics.start()

physics.setGravity( 0,2 )

physics.setDrawMode( “hybrid” )

local tail = {-117,12, -123,-46, -68,-13}

local bodyBack = {-89,-26, -61,-39, -20,-46, 20,-49, 42,27, -12,28, -66,16, -94,0}

local bodyFront = {20,-49, 71,-43, 107,-32, 121,-20, 126,-10, 108,5, 78,19, 43,27}

local finBack = {-39,23, -11,29, -10,41, -32,50}

local finFront = {-9,51, -11,28, 41,27, 15,42}

local fishFilter = { categoryBits=1, maskBits=2 }

local flakeFilter = { categoryBits=2, maskBits=1 }

local fish1 = display.newRect( 0,0,250,50 )

fish1.alpha = 0

fish1.x, fish1.y = display.contentWidth/2, display.contentHeight/2-60

physics.addBody( fish1, “kinematic”,

{ shape=tail, filter=fishFilter },

{ shape=bodyBack, filter=fishFilter },

{ shape=bodyFront, filter=fishFilter },

{ shape=finBack, filter=fishFilter },

{ shape=finFront, filter=fishFilter }

)

local fish2 = display.newRect( 0,0,250,50 )

fish2.alpha = 0

fish2.x, fish2.y = display.contentWidth/2, display.contentHeight/2+50

physics.addBody( fish2, “kinematic”,

{ shape=tail, filter=fishFilter },

{ shape=bodyBack, filter=fishFilter },

{ shape=bodyFront, filter=fishFilter },

{ shape=finBack, filter=fishFilter },

{ shape=finFront, filter=fishFilter }

)

local function stopFlake( event )

    local thisFlake = event.source.parent  --this references the flake on which the timer is a property

    thisFlake:setFillColor( 1 )

    thisFlake:setLinearVelocity( 0,0 )

    physics.removeBody( thisFlake )

end

local function flakeCollide( self, event )

    if ( event.phase == “began” ) then

        if ( self.collCount == 0 ) then  --this means that flake has NO contact with another element

            self:setFillColor( 1,0,0.2 )  --change flake color to red

            --print( “LAND TIMER STARTED” )

            self.landTimer = timer.performWithDelay( math.random(200,1200), stopFlake )

            self.landTimer.parent = self

            self.linearDamping = 2  --increase linear damping to make flake slow down

        end

        self.collCount = self.collCount+1  --increment self.collCount

    elseif ( event.phase == “ended” ) then

        self.collCount = self.collCount-1  --decrement self.collCount

        if ( self.collCount == 0 ) then  --this means that the flake is currently touching NO other elements

            self:setFillColor( 0,0.8,0.4 )  --change flake color to green

            --print( “LAND TIMER CANCELLED” )

            timer.cancel( self.landTimer )

            self.linearDamping = 0  --reset linear damping to make flake fall as normal

        end

    end

end

local function spawnFlake()

    local flake = display.newCircle( 0,0,8 )

    flake.x = math.random( fish1.contentBounds.xMin+20, fish1.contentBounds.xMax-20 )

    flake.y = display.contentHeight/2-160

    flake:setFillColor( 0,0.8,0.4 )  --set flake color to green initially

    flake.collCount = 0  --flake has not collided with anything yet, so set to 0

    flake.landTimer = nil

    physics.addBody( flake, { radius=2, isSensor=true, filter=flakeFilter } )

    flake.collision = flakeCollide

    flake:addEventListener( “collision”, flake )

end

timer.performWithDelay( 200, spawnFlake, 80 )

[/lua]

Hi Brent, Thank you for your time and your suggestion. 

  1. I changed some of the characteristics in your example to more accurately emulate my intended behavior. First, I altered the origin of the flakes to spawn anywhere I tap. I changed the size of my physics bodies to have a radius of 1, unfortunately I cannot have a flake radius less than 4 or 5. With these characteristics combined with a faster fall (tested gravity at 5x), I still am getting the same issues. (See attached image).

I also tried setContinuous(true).

--timer.performWithDelay( 200, spawnFlake, 80 ) local function onWorldTouch(e) spawnFlake(e.x,e.y) return true; end
  1. No, resuming isn’t necessary. Restarting a timer is fine. I’m mostly concerned with the positioning of the snowflakes and the timing of their landing with respect to their collision ended phase.

Is there any hope?

Thank you so much.

Hi Jeff,

So basically, you get more flakes stopping outside/below the objects when their speed is increased, i.e. because of increased gravity?

I don’t think this is an issue with collision detection frequency, but you could try increasing the defaults and setting the flakes as “bullets” and see what happens:

http://docs.coronalabs.com/api/library/physics/setPositionIterations.html

http://docs.coronalabs.com/api/library/physics/setVelocityIterations.html

http://docs.coronalabs.com/api/type/Body/isBullet.html

Otherwise, I would suggest a little visual trick. If the flakes are falling downward onto the branches of a tree, instead of the physics body on the tree being traced exactly around the visual tree image, you could trim the body inwards slightly, but only on the bottom of the branch regions. That way, the few flakes that stop a short distance outside the physics body would still appear (to the user) to be laying on the tree image.

Hey Brent,

So basically, you get more flakes stopping outside/below the objects when their speed is increased, i.e. because of increased gravity?

It appears that it’s more prone to this when the speed is fast. I’m not too familiar how everything works “under the hood” so I can’t say with certainty, but my guess is that the step right before the “collision ended” is large. In other words, the sequence would go like this.

  1. Flake is already colliding with the tree / land timer has started / flake is still traveling down

  2. Due to high speed, the flake takes a large step to its next position, which in this circumstance, happens to be outside the tree’s physics body.

  3. The land timer happens to complete, which stops the flake.

  4. Collision end is triggered, which triggers the cancellation of the landTimer, which doesn’t do anything since the land timer has already completed.

Feel free to correct me if I’m wrong. And if I’m wrong, could you help me understand the situation a bit more?

I don’t think this is an issue with collision detection frequency, but you could try increasing the defaults and setting the flakes as “bullets” and see what happens:

http://docs.coronalabs.com/api/library/physics/setPositionIterations.html
http://docs.coronalabs.com/api/library/physics/setVelocityIterations.html
http://docs.coronalabs.com/api/type/Body/isBullet.html

I’ve tried all these options. As far as setPositionIterations and setVelocityIterations, I’ve tried an arguments of 1, 16 and even 50. Also, tried isBullet and setContinuous, but unfortunately I still get the issue (See attached image). Here’s a copy of your code, which I updated with these suggestions:

local physics = require( "physics" ) physics.start() physics.setGravity( 0,5 ) -- \<--------- GRAVITY AT physics.setDrawMode( "hybrid" ) physics.setContinuous( true ) -- \<----------- SET CONTINUOUS physics.setPositionIterations( 50 ) -- \<----------- SET POSITION ITERATIONS physics.setVelocityIterations( 50 ) -- \<----------- SET VELOCITY ITERATIONS local tail = {-117,12, -123,-46, -68,-13} local bodyBack = {-89,-26, -61,-39, -20,-46, 20,-49, 42,27, -12,28, -66,16, -94,0} local bodyFront = {20,-49, 71,-43, 107,-32, 121,-20, 126,-10, 108,5, 78,19, 43,27} local finBack = {-39,23, -11,29, -10,41, -32,50} local finFront = {-9,51, -11,28, 41,27, 15,42} local fishFilter = { categoryBits=1, maskBits=2 } local flakeFilter = { categoryBits=2, maskBits=1 } local fish1 = display.newRect( 0,0,250,50 ) fish1.alpha = 0 fish1.x, fish1.y = display.contentWidth/2, display.contentHeight/2-60 physics.addBody( fish1, "kinematic", { shape=tail, filter=fishFilter }, { shape=bodyBack, filter=fishFilter }, { shape=bodyFront, filter=fishFilter }, { shape=finBack, filter=fishFilter }, { shape=finFront, filter=fishFilter } ) local fish2 = display.newRect( 0,0,250,50 ) fish2.alpha = 0 fish2.x, fish2.y = display.contentWidth/2, display.contentHeight/2+50 physics.addBody( fish2, "kinematic", { shape=tail, filter=fishFilter }, { shape=bodyBack, filter=fishFilter }, { shape=bodyFront, filter=fishFilter }, { shape=finBack, filter=fishFilter }, { shape=finFront, filter=fishFilter } ) local function stopFlake( event ) local thisFlake = event.source.parent --this references the flake on which the timer is a property thisFlake:setFillColor( 1 ) thisFlake:setLinearVelocity( 0,0 ) physics.removeBody( thisFlake ) end local function flakeCollide( self, event ) if ( event.phase == "began" ) then if ( self.collCount == 0 ) then --this means that flake has NO contact with another element self:setFillColor( 1,0,0.2 ) --change flake color to red --print( "LAND TIMER STARTED" ) self.landTimer = timer.performWithDelay( math.random(1,1200), stopFlake ) self.landTimer.parent = self self.linearDamping = 2 --increase linear damping to make flake slow down end self.collCount = self.collCount+1 --increment self.collCount elseif ( event.phase == "ended" ) then self.collCount = self.collCount-1 --decrement self.collCount if ( self.collCount == 0 ) then --this means that the flake is currently touching NO other elements self:setFillColor( 0,0.8,0.4 ) --change flake color to green print( "LAND TIMER CANCELLED" ) timer.cancel( self.landTimer ) self.linearDamping = 0 --reset linear damping to make flake fall as normal end end end local function spawnFlake(x,y) local flake = display.newCircle( 0,0,4 ) flake.x = x flake.y = y flake:setFillColor( 0,0.8,0.4 ) --set flake color to green initially flake.collCount = 0 --flake has not collided with anything yet, so set to 0 flake.landTimer = nil physics.addBody( flake, { radius=1, isSensor=true, filter=flakeFilter } ) flake.isBullet = true -- \<------------------ IS BULLET flake.collision = flakeCollide flake:addEventListener( "collision", flake ) end --timer.performWithDelay( 200, spawnFlake, 80 ) local function onWorldTouch(e) spawnFlake(e.x,e.y) return true; end Runtime:addEventListener("touch", onWorldTouch)

Assuming there’s still nothing we can do (aside from my kill timer idea and offsetting the bottom of the physics body), maybe we can move on to the 2nd example? Do you have any suggestions for (or questions about) example 2?

On a side note, would you guys ever consider providing a “Polygon Contains Point” functionality? I feel like it would be extremely valuable.

Thank you for your time!

Hi Jeff,

I don’t think there’s much else to do for case #1. The collision events and timers are firing on each time step of the app, and there’s not much which can be done about that. I assume you have that set to 60 instead of 30, yes? That may improve things just slightly, but I think you’ll still get some flakes stopping outside of the tree unless you offset the bottom bounds of the tree branches as I suggested.

You could also consider a fairly backwards-sounding scenario. Instead of creating a body for the tree, create a multi-element body of everything except the tree (so, all of the space surrounding it). Then, you could use PRE-collisions on the flakes to potentially stop them before they ever left the portion of a branch (and in this “bizarro-world” scenario, that would actually be when the flake collided with the “non-tree” space.

For case #2, I’m curious why you’re using physics for this. If you’re “painting the tree”, why not just use a mask and snapshot painting methods as exhibited in our sample code?

Brent

@Brent Sorrentino,

Case #1: Ended up just using my slightly hacky solution with the kill timer.

Case #2: Ended up switching to using a mask like you proposed. Since the tip of the brush was offset, I had to offset the mask in the reverse direction to compensate. Originally, I didn’t use the mask to do this, because I was using it for something else that I did not want the mask to be offset for (fortunately, I found a work around). Also, the physics issues discussed above weren’t really foreseeable. Therefore, I tried to use physics bodies since I thought they would work. 

Thank you

Hi @jhow,

Thanks for the extremely detailed description. Physics challenges are fun to solve (for me at least) so let’s get started. :slight_smile:

My first question is each case is: are you using a collision “counter” on the objects (snowflakes or brush tip) in regards to the multi-element tree body? Remember that when dealing with multi-element bodies, you will get a “began” and “ended” phase for each element of each body that collides. This is a crucial aspect to understand, and if you’re not keeping count of this, that’s likely why your timers and such are being thrown into whacky behavior.

This tutorial describes it in greater detail… read the “Part II” in specific:

https://coronalabs.com/blog/2013/01/08/working-with-multi-element-physics-bodies/

Best regards,

Brent

Hi @Brent Sorrentino,

Yep. That is being handled. I pretty much followed that tutorial so my began handling only starts on the “began” on the first element that’s touched and the ended handling only happens on the “ended” of the last element touched.

Also, the tree is multi-element while the physics bodies for the snowflakes and the paint brush tip are just single circles.

Jeff

Hi Jeff,

OK, yes, but are you tracking how many collisions stack up and “stack down” as the object moves across the tree?

Brent

By this you’re referring to something along the lines of your “Managing the Count” section of your link, correct?

I think I have pretty much completed the same thing with a few minor changes. My code:

local function onBrushTipCollision(self, e) -- Multi-element Abstraction local eStates = self.elementStates local treeElement = e.otherElement if e.phase == "began" then if not eStates[treeElement] then eStates[treeElement] = 0 end if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn + 1 end eStates[treeElement] = eStates[treeElement] + 1 elseif e.phase == "ended" then eStates[treeElement] = eStates[treeElement] - 1 if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn - 1 end end -- Phase Handling if self.elementsIn \> 0 and not self.isColliding then print("BrushTip Collision: " .. e.phase) self.isColliding = true elseif self.elementsIn == 0 and self.isColliding then print("BrushTip Collision: " .. e.phase) self.isColliding = false end end brushTipCircle.collision = onBrushTipCollision brushTipCircle:addEventListener("collision", brushTipCircle)

The main difference here is that I am in the touch event of the brush object counting the elements of the “otherElement” (ie. the tree). In your example you do “selfElement”. Also, the conditionals in the phase handling are slightly different to handle my specific case. Does that answer your question?

Hi Jeff,

Preferably, let’s deal with the falling snowflakes case first. Can I see the code you’re using?

Brent

Here’s a bit of the code. It’s actually the least evil of the two examples since I was actually able to hack up some code to compensate a little bit for this situation:

local function spawnSnowflakeImg(type) local snowflakeImg = display.newImage(imageSheet, sheetInfo:getFrameIndex("Snowflake" .. type)); local clr = kSnowflakeData[mRand(#clrs)] snowflakeImg:setFillColor(clr[1], clr[2], clr[3], 1) snowflakeImg.rotation = mRand(-rot,rot) return snowflakeImg end local function spawnSnowflake(x, y) local snowflakeImg = spawnSnowflakeImg(sType) decorateGrp:insert(snowflakeImg) snowflakeImg.x, snowflakeImg.y = decorateGrp:contentToLocal(x,y); local bodyName = "Snowflake" physics.addBody(snowflakeImg, shapedefs.physicsData():get(bodyName)) table.insert(thrownObjs, snowflakeImg) snowflakeImg.elementStates = {} snowflakeImg.elementsIn = 0 snowflakeImg.isColliding = false local landTimer local function cancelLandTimer() if landTimer then timer.cancel(landTimer) end landTimer = nil; end local killBodyTimer local function cancelKillBodyTimer() if killBodyTimer then timer.cancel(killBodyTimer) end killBodyTimer = nil; end local function onLocalCollision(self, e) -- Multi-element Abstraction local eStates = self.elementStates local treeElement = e.otherElement if e.phase == "began" then if not eStates[treeElement] then eStates[treeElement] = 0 end if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn + 1 end eStates[treeElement] = eStates[treeElement] + 1 elseif e.phase == "ended" then eStates[treeElement] = eStates[treeElement] - 1 if eStates[treeElement] == 0 then self.elementsIn = self.elementsIn - 1 end end -- Phase Handling if self.elementsIn \> 0 and not self.isColliding then -- Collision Begins self.isColliding = true -- Slow down snowflake local vx, vy = snowflakeImg:getLinearVelocity() snowflakeImg:setLinearVelocity(0.5\*vx, 0.5\*vy) snowflakeImg.gravityScale = 0.85 -- Calculate a rough random time when to land the snowflake -- based upon vertical distance to bottom of tree (obtained from ray cast) local contentX, contentY = snowflakeImg:localToContent( e.x, e.y) local hits = physics.rayCast(contentX, kTable["yMax"]+100, contentX, kTable["yMin"], "closest") local hitDist if hits and hits[1] then hitDist = mAbs(hits[1].position.y - contentY) end local yDist = hitDist or 200 landTimer = timer.performWithDelay(mRand(0,4\*yDist), function(e) -- Stop snowflake in place snowflakeImg:setLinearVelocity(0, 0) snowflakeImg.gravityScale = 0 -- Currently delaying body removal due to the fact that sometimes the land timer is not cancelled in time killBodyTimer = timer.performWithDelay(600, function(e) physics.removeBody(snowflakeImg) end) end) elseif self.elementsIn == 0 and self.isColliding then -- Collision ends self.isColliding = false cancelLandTimer() cancelKillBodyTimer() -- Snowflake no longer touching tree --\> continue falling. snowflakeImg.gravityScale = 1 end end snowflakeImg.collision = onLocalCollision snowflakeImg:addEventListener("collision", snowflakeImg) end

You’ll see I delayed the body removal using a “kill body timer”. This will delay the body removal for the situations when the the snowflake stops outside the cake (ie. the land timer finishes, but the collision ended phase has not been resolved). If possible, preferably, I would like to accomplish this without the kill body timer. (ie. assuming I can be able to remove the body immediately).

Hi Jeff,

I think you may be over-complicating this a bit. :slight_smile: Try the following example that I put together.

Here are a few notes before you test it:

  1. Notice that I made the flake physics bodies (radius) smaller than the actual circle, which helps prevent the impression that the flake is just barely clinging to the bottom edge of the surface it’s on.

  2. This example doesn’t utilize pausing and resuming of the land timer, but is that really necessary? If a flake falls off the first object, and has a remaining land timer amount of (say) 115 milliseconds, is it crucial that it uses 115 milliseconds for the land timer when it falls onto the second object? My example just cancels the timer and starts a new timer (random between 200-1200).

[lua]

local physics = require( “physics” )

physics.start()

physics.setGravity( 0,2 )

physics.setDrawMode( “hybrid” )

local tail = {-117,12, -123,-46, -68,-13}

local bodyBack = {-89,-26, -61,-39, -20,-46, 20,-49, 42,27, -12,28, -66,16, -94,0}

local bodyFront = {20,-49, 71,-43, 107,-32, 121,-20, 126,-10, 108,5, 78,19, 43,27}

local finBack = {-39,23, -11,29, -10,41, -32,50}

local finFront = {-9,51, -11,28, 41,27, 15,42}

local fishFilter = { categoryBits=1, maskBits=2 }

local flakeFilter = { categoryBits=2, maskBits=1 }

local fish1 = display.newRect( 0,0,250,50 )

fish1.alpha = 0

fish1.x, fish1.y = display.contentWidth/2, display.contentHeight/2-60

physics.addBody( fish1, “kinematic”,

{ shape=tail, filter=fishFilter },

{ shape=bodyBack, filter=fishFilter },

{ shape=bodyFront, filter=fishFilter },

{ shape=finBack, filter=fishFilter },

{ shape=finFront, filter=fishFilter }

)

local fish2 = display.newRect( 0,0,250,50 )

fish2.alpha = 0

fish2.x, fish2.y = display.contentWidth/2, display.contentHeight/2+50

physics.addBody( fish2, “kinematic”,

{ shape=tail, filter=fishFilter },

{ shape=bodyBack, filter=fishFilter },

{ shape=bodyFront, filter=fishFilter },

{ shape=finBack, filter=fishFilter },

{ shape=finFront, filter=fishFilter }

)

local function stopFlake( event )

    local thisFlake = event.source.parent  --this references the flake on which the timer is a property

    thisFlake:setFillColor( 1 )

    thisFlake:setLinearVelocity( 0,0 )

    physics.removeBody( thisFlake )

end

local function flakeCollide( self, event )

    if ( event.phase == “began” ) then

        if ( self.collCount == 0 ) then  --this means that flake has NO contact with another element

            self:setFillColor( 1,0,0.2 )  --change flake color to red

            --print( “LAND TIMER STARTED” )

            self.landTimer = timer.performWithDelay( math.random(200,1200), stopFlake )

            self.landTimer.parent = self

            self.linearDamping = 2  --increase linear damping to make flake slow down

        end

        self.collCount = self.collCount+1  --increment self.collCount

    elseif ( event.phase == “ended” ) then

        self.collCount = self.collCount-1  --decrement self.collCount

        if ( self.collCount == 0 ) then  --this means that the flake is currently touching NO other elements

            self:setFillColor( 0,0.8,0.4 )  --change flake color to green

            --print( “LAND TIMER CANCELLED” )

            timer.cancel( self.landTimer )

            self.linearDamping = 0  --reset linear damping to make flake fall as normal

        end

    end

end

local function spawnFlake()

    local flake = display.newCircle( 0,0,8 )

    flake.x = math.random( fish1.contentBounds.xMin+20, fish1.contentBounds.xMax-20 )

    flake.y = display.contentHeight/2-160

    flake:setFillColor( 0,0.8,0.4 )  --set flake color to green initially

    flake.collCount = 0  --flake has not collided with anything yet, so set to 0

    flake.landTimer = nil

    physics.addBody( flake, { radius=2, isSensor=true, filter=flakeFilter } )

    flake.collision = flakeCollide

    flake:addEventListener( “collision”, flake )

end

timer.performWithDelay( 200, spawnFlake, 80 )

[/lua]