Complicated issues involving collision "ended" timing

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