Destroy / Remove Physics Objects on Collision

Hi, I have a problem with removing physics bodies (petals) that fall from the top of the screen to the bottom with gravity.  The petals are falling constantly on the title screen and because they aren’t being removed until the scene is changed the memory use just keeps going up until scene change.  I have set up a sensor off the bottom of the page and can get a collision to fire with the petal objects but then the simulator crashes with the error “Attempt to index global ‘Petal’ (a nil value)”.  Based on this I assume it means I am not addressing the Petal object correctly in the collision but I am not sure how to do this as the petals are randomly new to this and I am still learning how to code…

The code that creates the petals is (this works on its own):

-- Petal positioning layer.Petal = {} layer.gp\_Petal = display.newGroup() layer.c\_Petal = 0 sceneGroup:insert( layer.gp\_Petal) -- Multipliers for Petal local Petal\_m\_loop = 1 --1 plays multiplier forever local Petal\_m\_counter = 100; composer.Petal\_m\_restart = 100 composer.mt\_Petal = function(counter) physics.setGravity(math.random(9\*-1,9)/10, 4); layer.Petal[counter] = display.newImageRect( composer.imgDir.. "p13\_petal.png", 62, 50 ); layer.Petal[counter].x = math.random(0,2048) layer.Petal[counter].y = math.random(-50,-50) layer.Petal[counter].alpha = math.random(100,100) / 100 layer.Petal[counter].oldAlpha = 1 layer.Petal[counter].xScale = math.random(60,100) / 100 layer.Petal[counter].yScale = layer.Petal[counter].xScale layer.Petal[counter].rotation = math.random(0,360) layer.Petal[counter].gravityScale = 0.15 local pweight = math.random(1, 2); local physicsData = (require "p13\_fairies\_physics").physicsData(1.0); physics.addBody(layer.Petal[counter], "dynamic", physicsData:get("p13\_petal")); local a = pweight \* layer.Petal[counter].xScale; layer.Petal[counter].linearDamping = a; layer.Petal[counter].angularDamping = 0.8 layer.Petal[counter].myName = "Petal" layer.gp\_Petal:insert(layer.Petal[counter]) end local function ct\_Petal() layer.c\_Petal = layer.c\_Petal + 1 if composer.mt\_Petal ~= nil then composer.mt\_Petal( layer.c\_Petal) end if (layer.c\_Petal == composer.Petal\_m\_restart and Petal\_m\_loop == 1) then composer.timerStash.mt = timer.performWithDelay( 400, ct\_Petal, 100 ) composer.Petal\_m\_restart = layer.c\_Petal + Petal\_m\_counter end end composer.multi\_Petal = function() composer.timerStash.mt = timer.performWithDelay( 400, ct\_Petal, 100 ) end composer.multi\_Petal()

 and the code that sets up the sensor and attempts to destroy petals when they fall off screen which crashes the simulator is:

-- petal\_floor positioning layer.petal\_floor = display.newRect( 1024, 1600, 2048, 20 ) layer.petal\_floor.oriX = layer.petal\_floor.x; layer.petal\_floor.oriY = layer.petal\_floor.y layer.petal\_floor.oriXs = layer.petal\_floor.xScale; layer.petal\_floor.oriYs = layer.petal\_floor.yScale layer.petal\_floor:setFillColor (0, 0, 0) layer.petal\_floor.alpha = 1; layer.petal\_floor.oldAlpha = 1 sceneGroup:insert( 1, layer.petal\_floor); sceneGroup.petal\_floor = layer.petal\_floor physics.addBody(layer.petal\_floor, "static", { isSensor=true } ) layer.petal\_floor.myName = "petal\_floor" local function onCollision\_petal\_floor(self, event) if event.phase == "began" and event.other.myName == "Petal" then Petal:removeSelf() Petal = nil end end layer.petal\_floor.collision = onCollision\_petal\_floor layer.petal\_floor:addEventListener("collision", petal\_floor) 

Appreciate any help anyone can give me!

Defer the removal for one frame.

Replace this:

Petal:removeSelf()

with this

timer.performWithDelay(1, function() display.remove( Petal ) end )

Does this really work for people? Timers are a constant frustration to me. (and so I have my own non-timer ways of doing similar)

It’s unfortunate that Corona’s “main loop” isn’t documented, but a bit of experimenting will strongly suggest that both events and physics processing occur before enterFrame in a single pass of the loop. So you’d expect that setting a 0ms timer during physics processing would fire during THIS frame.

But that’s rarely the case: A 0ms timer will actually fire NEXT frame. And a mere 1ms timer won’t fire until TWO frames after the collision event! (assuming, as is likely on simulator, that less than 1ms elapsed between physics processing and enterFrame)

So good luck guessing which frame a 16ms or 17ms (trunc/round of 1/60fps) timer would fire on!! :smiley:

Those results from the Windows simulator, but it appears to vary by device, and apparently subject to “load” as well. Makes debugging timers darned near impossible if you really do indeed need to assure that something happens in a timely manner.

Curious what others see from something like this:

display.setStatusBar( display.HiddenStatusBar ) local physics = require("physics") local frameCount = 0 local verbose = false local collisionFrame = 0 local function onTimer() if (verbose) then print("onTimer @ " .. tostring(frameCount)) local elapsed = frameCount - collisionFrame print(" " .. tostring(elapsed) .. " frames have elapsed since collision event") verbose = false -- done reporting stuff end end local function onCollision(event) verbose = true -- start reporting stuff collisionFrame = frameCount print("onCollision @ " .. tostring(frameCount)) timer.performWithDelay(1, onTimer) end function onEnterFrame(event) if (verbose) then print("onEnterFrame: " .. tostring(frameCount)) end frameCount = frameCount + 1 end physics.start() physics.setDrawMode("hybrid") local floor = display.newRect(display.contentCenterX, display.contentHeight, display.contentWidth, 100) physics.addBody(floor, "static", {} ) local ball = display.newCircle(display.contentCenterX, display.contentCenterY, 20) physics.addBody(ball, "dynamic", { radius=20 } ) Runtime:addEventListener("enterFrame", onEnterFrame) Runtime:addEventListener("collision", onCollision)

I have managed to get it working.  The timer approach didn’t fix it and was not necessary in the end.  I am not a programmer so I will try to explain this in my pigeon-programming language… As I understand it my problem was that I wasn’t able to properly address the petal objects because they were defined as an index locally in a function higher up in the code and I was trying to address them in a collision event outside of that function.  Once I inserted the collision event code inside the petal object indexing function and fixed up a few other errors in my collision code it now works.  See attached the final code (apologies for the bad formatting).

 -- Petal positioning layer.Petal = {} layer.gp\_Petal = display.newGroup() layer.c\_Petal = 0 sceneGroup:insert( layer.gp\_Petal) layer.petal\_floor = display.newRect( sceneGroup, 1024, 1700, 2048, 100 ) layer.petal\_floor:setFillColor( 1 ) physics.addBody(layer.petal\_floor, "static", { isSensor=true } ) layer.petal\_floor.myName = "petal\_floor" -- Multipliers for Petal local Petal\_m\_loop = 1 --1 plays multiplier forever local Petal\_m\_counter = 100; composer.Petal\_m\_restart = 100 composer.mt\_Petal = function(counter) physics.setGravity(math.random(9\*-1,9)/10, 4); layer.Petal[counter] = display.newImageRect( composer.imgDir.. "p13\_petal.png", 62, 50 ); layer.Petal[counter].x = math.random(0,2048) layer.Petal[counter].y = math.random(-50,-50) layer.Petal[counter].alpha = math.random(100,100) / 100 layer.Petal[counter].oldAlpha = 1 layer.Petal[counter].xScale = math.random(60,100) / 100 layer.Petal[counter].yScale = layer.Petal[counter].xScale layer.Petal[counter].rotation = math.random(0,360) layer.Petal[counter].gravityScale = 0.15 local pweight = math.random(1, 2); local physicsData = (require "p13\_fairies\_physics").physicsData(1.0); physics.addBody(layer.Petal[counter], "dynamic", physicsData:get("p13\_petal")); local a = pweight \* layer.Petal[counter].xScale; layer.Petal[counter].linearDamping = a; layer.Petal[counter].angularDamping = 0.8 layer.Petal[counter].myName = "Petal" local function onLocalCollision (self, event) if event.phase == "began" then display.remove( layer.Petal[counter] ) layer.Petal[counter] = nil -- print("Petal Collision") end end layer.petal\_floor.collision = onLocalCollision layer.petal\_floor:addEventListener("collision", layer.petal\_floor) layer.Petal[counter].collision = onLocalCollision layer.Petal[counter]:addEventListener("collision", layer.Petal[counter]) layer.gp\_Petal:insert(layer.Petal[counter]) end local function ct\_Petal() layer.c\_Petal = layer.c\_Petal + 1 if composer.mt\_Petal ~= nil then composer.mt\_Petal( layer.c\_Petal) end if (layer.c\_Petal == composer.Petal\_m\_restart and Petal\_m\_loop == 1) then composer.timerStash.mt = timer.performWithDelay( 400, ct\_Petal, 100 ) composer.Petal\_m\_restart = layer.c\_Petal + Petal\_m\_counter end end composer.multi\_Petal = function() composer.timerStash.mt = timer.performWithDelay( 400, ct\_Petal, 100 ) end composer.multi\_Petal()

Hi all,

Removing physics bodies on collision should actually not require the “delay with timer” approach. Other physics-related actions certainly do require this method, but outright removal on collision should work directly in the same code block. If you find otherwise, please post your code so I can inspect.

Thanks,

Brent

Brent, with all due respect, you guys (Corona) have us chasing on tails between the docs and forum responses. You tell us not to do something or we’ll crash, so we do your suggested workaround, then you tell us we shouldn’t be doing that workaround. Huh?

From docs: http://docs.coronalabs.com/api/event/collision/index.html
“Removing the object or modifying the properties in the collision event could cause the simulator to crash.”

From guide: https://developer.coronalabs.com/content/game-edition-collision-detection
“Do not modify / create / destroy physics objects during a collision, on penalty of crashing.”

Not to mention that this forum is LITTERED with staff suggestions to use the performWithDelay() kludge.

So what’s the real deal?

Hi @davebollinger,

Please read my response carefully:

“Other physics-related actions certainly do require this method.”

That means that some actions do, for example, trying to deactivate a body on collision. The Simulator will flag you if you attempt to do this. However, direct removal of a body during a collision should be allowable without using a timer.

As for the second link, I thought that those were removed from our site well over a year ago, and I’m surprised it somehow re-surfaced. If you ever see a page that refers to “Game Edition”, or looks visually like that page, then it’s ancient and should not be trusted.

Brent

I don’t want to start a battle over this but… i did read your response, very carefully. Can we set aside the other actions that do require this method, and just ask about removal? We’re still left with two competing statements:

forum: “direct removal of a body during a collision should be allowable without using a timer”

docs: “Removing the object … in the collision event could cause the simulator to crash.”

Yours is worded with a “should”, docs are worded with a “could”, so neither seems definitive.

Think about it from our perspective: any self-respecting developer who’s gonna release product to the masses wouldn’t DARE do something that official docs say “could crash”. If some staff took the time to write it down in official docs, then it must’ve been pretty important, yes?

So, if staff then say the opposite in the forum, shouldn’t we at least question it? I don’t know your ‘hierarchy’ at Corona, so this isn’t personal, but the default for me is that official written docs should initially win the credibility battle versus forum. If I’m going to ‘countermand’ docs with a forum post, then it better be worded MORE strongly than the docs - fewer could/should’s. Fair?

Dave

Hi Dave,

I made a quick test of this in 2 minutes. Direct removal on collision does not crash, so there is the definitive answer.

[lua]

local circle = display.newCircle( display.contentCenterX-200, display.contentCenterY-100, 50 )

physics.addBody( circle, “dynamic”, { radius=100 } )

circle.gravityScale = 0

circle:applyForce( 20, 0 )

local circle2 = display.newCircle( display.contentCenterX+200, display.contentCenterY-100, 50 )

physics.addBody( circle2, “static”, { radius=100 } )

local function onLocalCollision( self, event )

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

      display.remove( self )

   end

end

circle.collision = onLocalCollision

circle:addEventListener( “collision”, circle )

[/lua]

I did not write all of the documentation myself, although improving it is one of my ongoing tasks. I’m often finding little things that I feel should not be in there, or should be stated more clearly. Many developers use the feedback buttons (Like it, Hate it, etc.) at the bottom of any documentation page to alert us to any issues, and I work through the queue whenever I get an opportunity. So, I urge you to use the feedback buttons as well, since they will get logged internally, while stating documentation issues in the forums will likely get lost.

Brent

Defer the removal for one frame.

Replace this:

Petal:removeSelf()

with this

timer.performWithDelay(1, function() display.remove( Petal ) end )

Does this really work for people? Timers are a constant frustration to me. (and so I have my own non-timer ways of doing similar)

It’s unfortunate that Corona’s “main loop” isn’t documented, but a bit of experimenting will strongly suggest that both events and physics processing occur before enterFrame in a single pass of the loop. So you’d expect that setting a 0ms timer during physics processing would fire during THIS frame.

But that’s rarely the case: A 0ms timer will actually fire NEXT frame. And a mere 1ms timer won’t fire until TWO frames after the collision event! (assuming, as is likely on simulator, that less than 1ms elapsed between physics processing and enterFrame)

So good luck guessing which frame a 16ms or 17ms (trunc/round of 1/60fps) timer would fire on!! :smiley:

Those results from the Windows simulator, but it appears to vary by device, and apparently subject to “load” as well. Makes debugging timers darned near impossible if you really do indeed need to assure that something happens in a timely manner.

Curious what others see from something like this:

display.setStatusBar( display.HiddenStatusBar ) local physics = require("physics") local frameCount = 0 local verbose = false local collisionFrame = 0 local function onTimer() if (verbose) then print("onTimer @ " .. tostring(frameCount)) local elapsed = frameCount - collisionFrame print(" " .. tostring(elapsed) .. " frames have elapsed since collision event") verbose = false -- done reporting stuff end end local function onCollision(event) verbose = true -- start reporting stuff collisionFrame = frameCount print("onCollision @ " .. tostring(frameCount)) timer.performWithDelay(1, onTimer) end function onEnterFrame(event) if (verbose) then print("onEnterFrame: " .. tostring(frameCount)) end frameCount = frameCount + 1 end physics.start() physics.setDrawMode("hybrid") local floor = display.newRect(display.contentCenterX, display.contentHeight, display.contentWidth, 100) physics.addBody(floor, "static", {} ) local ball = display.newCircle(display.contentCenterX, display.contentCenterY, 20) physics.addBody(ball, "dynamic", { radius=20 } ) Runtime:addEventListener("enterFrame", onEnterFrame) Runtime:addEventListener("collision", onCollision)

I have managed to get it working.  The timer approach didn’t fix it and was not necessary in the end.  I am not a programmer so I will try to explain this in my pigeon-programming language… As I understand it my problem was that I wasn’t able to properly address the petal objects because they were defined as an index locally in a function higher up in the code and I was trying to address them in a collision event outside of that function.  Once I inserted the collision event code inside the petal object indexing function and fixed up a few other errors in my collision code it now works.  See attached the final code (apologies for the bad formatting).

 -- Petal positioning layer.Petal = {} layer.gp\_Petal = display.newGroup() layer.c\_Petal = 0 sceneGroup:insert( layer.gp\_Petal) layer.petal\_floor = display.newRect( sceneGroup, 1024, 1700, 2048, 100 ) layer.petal\_floor:setFillColor( 1 ) physics.addBody(layer.petal\_floor, "static", { isSensor=true } ) layer.petal\_floor.myName = "petal\_floor" -- Multipliers for Petal local Petal\_m\_loop = 1 --1 plays multiplier forever local Petal\_m\_counter = 100; composer.Petal\_m\_restart = 100 composer.mt\_Petal = function(counter) physics.setGravity(math.random(9\*-1,9)/10, 4); layer.Petal[counter] = display.newImageRect( composer.imgDir.. "p13\_petal.png", 62, 50 ); layer.Petal[counter].x = math.random(0,2048) layer.Petal[counter].y = math.random(-50,-50) layer.Petal[counter].alpha = math.random(100,100) / 100 layer.Petal[counter].oldAlpha = 1 layer.Petal[counter].xScale = math.random(60,100) / 100 layer.Petal[counter].yScale = layer.Petal[counter].xScale layer.Petal[counter].rotation = math.random(0,360) layer.Petal[counter].gravityScale = 0.15 local pweight = math.random(1, 2); local physicsData = (require "p13\_fairies\_physics").physicsData(1.0); physics.addBody(layer.Petal[counter], "dynamic", physicsData:get("p13\_petal")); local a = pweight \* layer.Petal[counter].xScale; layer.Petal[counter].linearDamping = a; layer.Petal[counter].angularDamping = 0.8 layer.Petal[counter].myName = "Petal" local function onLocalCollision (self, event) if event.phase == "began" then display.remove( layer.Petal[counter] ) layer.Petal[counter] = nil -- print("Petal Collision") end end layer.petal\_floor.collision = onLocalCollision layer.petal\_floor:addEventListener("collision", layer.petal\_floor) layer.Petal[counter].collision = onLocalCollision layer.Petal[counter]:addEventListener("collision", layer.Petal[counter]) layer.gp\_Petal:insert(layer.Petal[counter]) end local function ct\_Petal() layer.c\_Petal = layer.c\_Petal + 1 if composer.mt\_Petal ~= nil then composer.mt\_Petal( layer.c\_Petal) end if (layer.c\_Petal == composer.Petal\_m\_restart and Petal\_m\_loop == 1) then composer.timerStash.mt = timer.performWithDelay( 400, ct\_Petal, 100 ) composer.Petal\_m\_restart = layer.c\_Petal + Petal\_m\_counter end end composer.multi\_Petal = function() composer.timerStash.mt = timer.performWithDelay( 400, ct\_Petal, 100 ) end composer.multi\_Petal()

Hi all,

Removing physics bodies on collision should actually not require the “delay with timer” approach. Other physics-related actions certainly do require this method, but outright removal on collision should work directly in the same code block. If you find otherwise, please post your code so I can inspect.

Thanks,

Brent

Brent, with all due respect, you guys (Corona) have us chasing on tails between the docs and forum responses. You tell us not to do something or we’ll crash, so we do your suggested workaround, then you tell us we shouldn’t be doing that workaround. Huh?

From docs: http://docs.coronalabs.com/api/event/collision/index.html
“Removing the object or modifying the properties in the collision event could cause the simulator to crash.”

From guide: https://developer.coronalabs.com/content/game-edition-collision-detection
“Do not modify / create / destroy physics objects during a collision, on penalty of crashing.”

Not to mention that this forum is LITTERED with staff suggestions to use the performWithDelay() kludge.

So what’s the real deal?

Hi @davebollinger,

Please read my response carefully:

“Other physics-related actions certainly do require this method.”

That means that some actions do, for example, trying to deactivate a body on collision. The Simulator will flag you if you attempt to do this. However, direct removal of a body during a collision should be allowable without using a timer.

As for the second link, I thought that those were removed from our site well over a year ago, and I’m surprised it somehow re-surfaced. If you ever see a page that refers to “Game Edition”, or looks visually like that page, then it’s ancient and should not be trusted.

Brent

I don’t want to start a battle over this but… i did read your response, very carefully. Can we set aside the other actions that do require this method, and just ask about removal? We’re still left with two competing statements:

forum: “direct removal of a body during a collision should be allowable without using a timer”

docs: “Removing the object … in the collision event could cause the simulator to crash.”

Yours is worded with a “should”, docs are worded with a “could”, so neither seems definitive.

Think about it from our perspective: any self-respecting developer who’s gonna release product to the masses wouldn’t DARE do something that official docs say “could crash”. If some staff took the time to write it down in official docs, then it must’ve been pretty important, yes?

So, if staff then say the opposite in the forum, shouldn’t we at least question it? I don’t know your ‘hierarchy’ at Corona, so this isn’t personal, but the default for me is that official written docs should initially win the credibility battle versus forum. If I’m going to ‘countermand’ docs with a forum post, then it better be worded MORE strongly than the docs - fewer could/should’s. Fair?

Dave

Hi Dave,

I made a quick test of this in 2 minutes. Direct removal on collision does not crash, so there is the definitive answer.

[lua]

local circle = display.newCircle( display.contentCenterX-200, display.contentCenterY-100, 50 )

physics.addBody( circle, “dynamic”, { radius=100 } )

circle.gravityScale = 0

circle:applyForce( 20, 0 )

local circle2 = display.newCircle( display.contentCenterX+200, display.contentCenterY-100, 50 )

physics.addBody( circle2, “static”, { radius=100 } )

local function onLocalCollision( self, event )

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

      display.remove( self )

   end

end

circle.collision = onLocalCollision

circle:addEventListener( “collision”, circle )

[/lua]

I did not write all of the documentation myself, although improving it is one of my ongoing tasks. I’m often finding little things that I feel should not be in there, or should be stated more clearly. Many developers use the feedback buttons (Like it, Hate it, etc.) at the bottom of any documentation page to alert us to any issues, and I work through the queue whenever I get an opportunity. So, I urge you to use the feedback buttons as well, since they will get logged internally, while stating documentation issues in the forums will likely get lost.

Brent