Correct way to remove physics object due to collision?

It appears that many posts in this forum regarding the removal of physics objects due to collisions appear to differ from the documentation. The collision detection guide (http://developer.anscamobile.com/content/game-edition-collision-detection) specifically mentions “…physics engine will reliably crash during a collision if Corona code attempts to modify objects still involved in the collision…”.

I see that many posts use the removeSelf() method to remove an object involved in the collision event. Is the documentation up to date? Should the objects to be removed be flagged for removal later (e.g. during the next enterFrame event)?

Thanks in advance.

Mike [import]uid: 101075 topic_id: 18290 reply_id: 318290[/import]

Hi Mike,

Yes - this means that the object should not be removed during the collision event.

The easiest way of handling this is to simply use [lua]if event.phase == “ended”[/lua].

Peach :slight_smile: [import]uid: 52491 topic_id: 18290 reply_id: 70105[/import]

If I remove the display object (removeSelf()), do I also need to remove the physics object (physics.removeBody())? Attempting to remove the physics body in the ended phase results in an error:

ERROR: physics.removeBody() cannot be called when the world is locked and in the middle of number crunching, such as during a collision event.

Mike [import]uid: 101075 topic_id: 18290 reply_id: 76411[/import]

i always use “began” phase of collision for all type of things, such as removing an object

[lua]local function onCollision(event)
if event.phase == “began” then
display.remove(event.other)
event.other=nil
end
end[/lua] [import]uid: 16142 topic_id: 18290 reply_id: 76416[/import]

As described on the physics page you need to add a slight delay before removing objects in a collision event.

[code]
–In collision handler

local function removeAfterDelay()
display.remove(objectToRemove)
end

timer.performWithDelay(2, removeAfterDelay)
[/code] [import]uid: 84637 topic_id: 18290 reply_id: 76492[/import]

I do not have a problem removing the display object within a collision handler. I am having trouble removing the physics body. Here is a typical handler…

[lua] function shape:collision(e)
print(tostring(system.getTimer()) … ‘\tshape collision event: ‘… tostring(e.phase))
if(e.phase == “began”) then
if(self.isVisible) then
self.isVisible = false
end
end
if(e.phase == “ended”) then
print(tostring(system.getTimer()) … ‘\t’…self.type …’ [’…tostring(self)…’] collided with ’ … e.other.type… ’ element: ’ …tostring(e.otherElement))
function cleanUp()
print(tostring(system.getTimer()) … ‘\tcleaning up ‘…self.type …’ [’…tostring(self)…’] collision with ’ … e.other.type… ’ element: ’ …tostring(e.otherElement))
physics:removeBody(self)
self:removeSelf()
end
timer.performWithDelay(10, cleanUp,1 )

end
end[/lua]

When I do the physics.removeBody and removeSelf without the timer delay, all appears fine on the application, but Corona terminal has the same error I posted earlier. If I use the timer approach, I get this error:

bad argument #-2 to ‘removeBody’ (Proxy expected, got nil)

This is driving me crazy. I must be missing something. Any ideas?

Here is a full main.lua file that demonstrates the problem:
[lua]display.setStatusBar( display.HiddenStatusBar)
_H = display.contentHeight
_W = display.contentWidth

local physics = require “physics”

function dropShape()
local radius = 18
local shape = display.newCircle(_W/2, 46, radius )
shape.type = “shape”
function shape:collision(e)
print(tostring(system.getTimer()) … ‘\tshape collision event: ‘… tostring(e.phase))
if(e.phase == “began”) then
if(self.isVisible) then
self.isVisible = false
end
end
if(e.phase == “ended”) then
print(tostring(system.getTimer()) … ‘\t’…self.type …’ [’…tostring(self)…’] collided with ’ … e.other.type… ’ element: ’ …tostring(e.otherElement))
function cleanUp()
print(tostring(system.getTimer()) … ‘\tcleaning up ‘…self.type …’ [’…tostring(self)…’] collision with ’ … e.other.type… ’ element: ’ …tostring(e.otherElement))
physics:removeBody(self)
self:removeSelf()
end
timer.performWithDelay(10, cleanUp,1 )

end
end

shape:addEventListener(“collision”, shape);

physics.addBody(shape,{radius = radius})
end

function setUpCatcher()
catcher = display.newRect(0, 0, 64, 20)
catcher.type = “catcher”
catcher:setFillColor(255, 0, 0)
catcher:setReferencePoint(display.BottomCenterReferencePoint);
catcher.x = _W/2
catcher.y = _H-20
function catcher:collision(e)
print(tostring(system.getTimer()) …’\tcatcher collision phase: '… tostring(e.phase))
end
catcher:addEventListener(“collision”, catcher);

local density = 0.3
local friction = 0
local bounce = 0.5
local catcherShape = { }
catcherShape[“64x48”] = {
{ – Left
density=density, friction=friction, bounce=bounce,
shape={
-31,-22,
-27,-22,
-17,18,
-20,21
}
},
{ – Bottom
density=density, friction=friction, bounce=bounce,
shape={
-17,18,
15,18,
18,21,
-20,21
}
},
{-- Right
density=density, friction=friction, bounce=bounce,
shape={
27,-22,
31,-22,
18,21,
15,18
}
}
}

physics.addBody(catcher, “kinematic”, unpack(catcherShape[“64x48”]));

end
function main()
physics.start(true)
physics.setDrawMode(“hybrid”);

setUpCatcher()
dropShape()
end
main()[/lua] [import]uid: 101075 topic_id: 18290 reply_id: 76654[/import]

I am not an expert here, but do you really need to remove it from physics? If the object is removed, wouldn’t it be removed from all instances automaticly then?

Joakim [import]uid: 81188 topic_id: 18290 reply_id: 76662[/import]

That was one of my unanswered questions a few weeks ago. Since you suggested it I looked at it that question again. I can see that (by using physics hybrid mode), the physics body does appear to be removed with self:removeSelf().

Thanks! [import]uid: 101075 topic_id: 18290 reply_id: 76668[/import]

Great and happy programming!

Joakim [import]uid: 81188 topic_id: 18290 reply_id: 76669[/import]

Also it’s…

[code]
physics.removeBody(body)

not

physics:removeBody(body)
[/code] [import]uid: 84637 topic_id: 18290 reply_id: 76691[/import]

I need to remove the physics body but not the entire object (which is still being looked for in another enterFrame event). I just need to be able to put something in the same spot that the other body just vacated. I get this error:

WARNING: physics.removeBody() given a display object that is not a physics object.
WARNING: physics.removeBody() given a display object that is not a physics object.

even with a timer delay:

function addScore()  
\_G.score2=\_G.score2+1000  
 scoreText.text = "score: "..\_G.score2  
 --body:removeSelf()  
 physics.removeBody(acorn2)  
 acorn2.y=-1000;  
 Runtime:addEventListener("enterFrame", checkScore)  
 end  
  
local function acornCollided(self, event)  
 body = event.other  
 if(event.phase == "ended") then  
 if body.type == "acorn2" then  
 Runtime:removeEventListener("enterFrame", checkScore)  
 timer.performWithDelay(9000,addScore)  
 end  
 end  
end  
  
ledgeBase.collision = acornCollided  
ledgeBase:addEventListener("collision", ledgeBase)  

thanks for any assistance in this matter… [import]uid: 124116 topic_id: 18290 reply_id: 86603[/import]

Hi Mike -

Is this being called more than once, perhaps?

A print statement immediately before the remove would be handy to work out whether or not this is the case.

Can you let me know, please?

Peach :slight_smile: [import]uid: 52491 topic_id: 18290 reply_id: 86636[/import]

mkelly,

Really missing a few pieces of your code to know exactly what your trying to accomplish but I made a few modifications to your code that might help.

Jeff

function addScore(body)  
 -- body.x and body.y now contain the position of the collided object  
 \_G.score2=\_G.score2+1000  
 scoreText.text = "score: "..\_G.score2  
  
 -- Now use body.x and body.y to position the new object  
 newObject.x = body.x  
 newObject.y = body.y  
  
 -- This is not needed because removeSelf() will take care of it  
 -- physics.removeBody(body)  
  
 -- I guess your trying to move the object out of view  
 acorn2.y=-1000;  
  
 -- If you want to delete the 'body' object  
 body:removeSelf()  
  
 Runtime:addEventListener("enterFrame", checkScore)  
end  
  
local function acornCollided(self, event)  
 local body = event.other  
 if(event.phase == "ended") then  
 if body.type == "acorn2" then  
 Runtime:removeEventListener("enterFrame", checkScore)  
 timer.performWithDelay(9000,function() addScore(body) end)  
 end  
 end  
end  
  
ledgeBase.collision = acornCollided  
ledgeBase:addEventListener("collision", ledgeBase)  

[import]uid: 14119 topic_id: 18290 reply_id: 86658[/import]

Sorry I missed something…

acorn2.y=-1000;  

should be …

body.y=-1000;  

[import]uid: 14119 topic_id: 18290 reply_id: 86661[/import]

hi Jeff,

I guess all I needed was to change the body.y position for what I was trying to accomplish: get a physics body out of the way so another physics body could be shoved into its place.

thanks!!!

thanks, Peach also for you prompt reply. I wasn’t calling it anywhere else; I guess it was just firing too quickly?

I appreciate both of you! [import]uid: 124116 topic_id: 18290 reply_id: 86689[/import]

Jeff, that’s superb :slight_smile:

Mike, apologies, I could have been clearer - I wasn’t sure if it was being called in another spot, only wondering if indeed it was being called more than once in a short period of time.

In any case am very happy to see it resolved :slight_smile: [import]uid: 52491 topic_id: 18290 reply_id: 86781[/import]

thanks, Peach! I did eventually get rid of it altogether without tripping further calls with a second time delay call in the addScore script:

timer.performWithDelay(1000,physics.removeBody(body))  

btw, do you know if there’s an isInWorld kind of call that can detect if a rigidBody still exists in the scene? [import]uid: 124116 topic_id: 18290 reply_id: 86788[/import]

I found a workaround but am still interested:

function addScore()  
body.y=-2000;  
rigidBodyCount=rigidBodyCount - 1  
print(rigidBodyCount)  
timer.performWithDelay(1000,physics.removeBody(body))  
if \_G.score2\>=1750 and rigidBodyCount == 1 then  
win();  
elseif (\_G.score2\>=3000) then  
win();  
end  
 end  

[import]uid: 124116 topic_id: 18290 reply_id: 86797[/import]

Not for counting bodies, no - you’d use a table or count as you are doing.

If you want to check if a specific object is a physics body I think there’s an API for that - let me know if that’s an option and I can try to find it. (Unless I dreamed it up in which case that wont happen.)

Peach :wink: [import]uid: 52491 topic_id: 18290 reply_id: 86822[/import]

Hi mKelly.

You can probably use 1>2 rather than 1000 in your timer.performWIthDelay call. 1000 = 1 second, [import]uid: 84637 topic_id: 18290 reply_id: 86842[/import]