LiquidFun, Collision and Removal of Objects

Hello everyone! I’m quite new to Corona SDK and I’m currently facing a problem which I can’t find a solution for. 

Imagine a scene with a ground and a simple crate object on it. The crate has a physics body and a collision event listener attached. 

The plan is to let LiquidFun particles rain down on the crate and after a defined count of collisions I’d like to remove the crate from the scene. I read in the documentation of Corona SDK and Box2D, that modifying a physical object (like the crate) during collision events will most likely force the application to crash, because the collisions are still in the calculation phase. 

I found a Box2D C++ tutorial in which the author shows how to safely remove physical objects. The idea is to not remove the objects immediately, but instead add them to an array/table and remove all objects inside this table between the time steps for physics calculations. So the objects will be removed when no physics calculations are in progress. 

Since the default physics time step in Corona is the frame rate, I thought the best place to put the object removal would be in the enterFrame event. So I just added an enterFrame event listener to the runtime and let it call my method for removing all objects which are queued for removal. 

Sadly, my app still crashes. Am I missing something? Is there a more suitable place to put my object removal method? 

Maybe the problem is a different one. When the object is ready to be deleted, it may happen that there are still other LiquidFun particles colliding with the object. But I think there will never be a point in time when the crate is ready to be deleted and does NOT have particle collisions happening. So how to remove the object? 

I would be happy for any idea.

generally you can remove the object after a 1>10 millisecond to second delay using a timer.performWithDelay, within your collision handler

Good point, but I already tried this in different ways and with different delays.

The Liquid Fun “particle rain” is more or less continuous in my scene. As long as the crate exists, there will always be particles colliding with it. 

When the “killing” particle hits the crate, I can of course set a delay to delete the crate. The killing collision event should be over then. But other particles will still be colliding with the crate when the final removal after the delay happens. 

I also tried to first remove the collision event listener, so the crate will not listen to particle collision anymore. But it seems like already started collision events will be calculated to their ended phase although the event listener is no longer existent. Please correct me if I’m wrong here because I just guess. 

We can look at the whole scene with just two particles to make it simpler. The first particle hits the crate and should destroy it. The second particle landed on the crate and is still there. Is it not possible to remove the crate as long as the second particle’s collision event is active?

Or is there maybe a way to cancel all active collision events of an object before removing it?

EDIT: 

According to this documentation:

http://docs.coronalabs.com/guide/physics/collisionDetection/index.html

I am allowed to call the object:removeSelf() method within a collision event. So why does the app crash then? 

Alright, I created a new simple test app. I added two crates (physics body, static) and a quite big rectangle of Liquid Fun particles falling down onto the crates right from the start. 

I’m removing the event listener of the crates during the collision event began phase and I also call the object:removeSelf() method immediately without any delay. It works! 

I got the feeling that there is something in my “old” code messing with the collision events at some point. From my current point of view I’m chasing a bug in my code and not a general problem with object removal during collision events. 

I’ll let you know when I got news.


UPDATE

Well it works as long as the crates are on the same y coordinates with the same removal point in time. When setting different removal times, the app gets stuck at the first crate removal. I’ll give it another try tomorrow. Maybe I just need a break.

I was able to get closer to the actual problem. First things first. 

When there is only one object in the scene with an event listener to the particleCollision, the colliding object can be removed during the collision event without any problems. Not even a delay is needed. Right before the removal I also remove the event listener, just to be sure the object does no longer collide. 

As soon as there is a second object in the scene also listening to the particleCollision, bad things begin to happen. It doesn’t even matter if the listener methods are the same or completely different ones. When the removal of both objects happens at nearly the same time (± 100 ms), the removal works but you still see the app stuttering at that point in time. When the gap between the removals becomes bigger, the app will surely crash. 

I have no great knowledge of Corona. Is it possible, that you can not remove an object colliding with a particle system, while other objects still collide with or listen to the same particle system? 

Would be great to know if this is a bug which will be fixed in the future or if my problem is not solvable at all. 

Hi @wabugi,

I’m curious to see your code and how you’ve set this all up. Can you post it, as “minimalist” as possible for examination? i.e. please remove it from any Composer/Storyboard framework if it’s done that way, remove any redundant code, etc.

Thanks,

Brent

Here is my sample code in a simple version. Don’t forget to change the image file names at the top. The code works as long as you uncomment the last line. 

I know there are maybe unneeded loops in my listener function. I just wanted to prevent some errors.

local physics = require "physics" physics.start() --------------------------------------- -- IMPORTANT: Set your image names here --------------------------------------- local particleImageName = "water-particle-4.png" local crateImageName = "crate.png" -- A table containing functions etc. Makes it easier to call them from anywhere. local t = {} -- Basic particle system setup -- local myParticleSystem = physics.newParticleSystem( { filename = particleImageName, radius = 8, imageRadius = 16 } ) -- Spawning a block of water particles -- local waterblock = myParticleSystem:createGroup( { flags = { "water", "fixtureContactListener" }, x = 300, y = 100, color = { 0, 0.5, 1, 1 }, halfWidth = 200, halfHeight = 400 } ) -- Listener function for the crates -- t.objectParticleCollisionListener = function ( event ) -- Make sure the object is still there. if ( event.object ~= nil ) then -- Store an object reference in a local variable. local target = event.object if ( event.phase == "began" ) then -- Just check if the object has a health pool at all. if target.healthPool then -- Substract 1 health for every collision as long as the health pool is bigger than 0. if target.healthPool \> 0 then target.healthPool = target.healthPool - 1 end -- The health pool is now 0 if target.healthPool == 0 then print( target.name .. " health is 0." ) -- Check if the object is already flagged for removal. if target.garbage ~= true then print( target.name .. " will be removed now." ) -- Remove the event listener from the object first, to prevent future collision events. target:removeEventListener( "particleCollision", t.objectParticleCollisionListener ) -- Flag the object for removal to prevent the function from trying to remove it more than once. target.garbage = true -- Finally remove the object from the scene. display.remove(target) -- Set the object to nil, just to be sure there is nothing left. target = nil end end end end end -- Return true in every case to complete the collision event return true end -- Some params for the crates -- local crateParams = { image = crateImageName, width = 50, height = 50, density = 5.0, friction = 0.3, bounce = 0.2 } -- Spawn crate 1 -- local crate = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate.x, crate.y = 300, 600 physics.addBody( crate, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) -- Setting up a health pool which can count down in the listener function. crate.healthPool = 500 -- Just a boolean value to see if the crate is already marked for removal. crate.garbage = false crate.bodyType = "static" crate.name = "Crate 01" crate:addEventListener( "particleCollision", t.objectParticleCollisionListener ) -- Spawn crate 2 -- local crate2 = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate2.x, crate2.y = 360, 800 physics.addBody( crate2, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) crate2.healthPool = 500 crate2.garbage = false crate2.bodyType = "static" crate2.name = "Crate 02" -- IMPORTANT -- If you uncomment the last line, the second crate receives its event listener. -- The app will crash, at least in my tests. --crate2:addEventListener( "particleCollision", t.objectParticleCollisionListener )

Hi @wabugi,

Can you try removing the function(s) from that “t” table? I know you’re trying to keep them organized, but this may be causing some issue with the removal of the listener and such. Please structure it just as a local function somewhere above, and see if you still get the crash.

Thanks,

Brent

Here you go. The behavior didn’t change. Still crashing when 2 objects are listening. 

local physics = require "physics" physics.start() --------------------------------------- -- IMPORTANT: Set your image names here --------------------------------------- local particleImageName = "water-particle-4.png" local crateImageName = "crate.png" -- Basic particle system setup -- local myParticleSystem = physics.newParticleSystem( { filename = particleImageName, radius = 8, imageRadius = 16 } ) -- Spawning a block of water particles -- local waterblock = myParticleSystem:createGroup( { flags = { "water", "fixtureContactListener" }, x = 300, y = 100, color = { 0, 0.5, 1, 1 }, halfWidth = 200, halfHeight = 400 } ) -- Listener function for the crates -- local function objectParticleCollisionListener( event) -- Make sure the object is still there. if ( event.object ~= nil ) then -- Store an object reference in a local variable. local target = event.object if ( event.phase == "began" ) then -- Just check if the object has a health pool at all. if target.healthPool then -- Substract 1 health for every collision as long as the health pool is bigger than 0. if target.healthPool \> 0 then target.healthPool = target.healthPool - 1 end -- The health pool is now 0 if target.healthPool == 0 then print( target.name .. " health is 0." ) -- Check if the object is already flagged for removal. if target.garbage ~= true then print( target.name .. " will be removed now." ) -- Remove the event listener from the object first, to prevent future collision events. target:removeEventListener( "particleCollision", objectParticleCollisionListener ) -- Flag the object for removal to prevent the function from trying to remove it more than once. target.garbage = true -- Finally remove the object from the scene. display.remove(target) -- Set the object to nil, just to be sure there is nothing left. target = nil end end end end end -- Return true in every case to complete the collision event return true end -- Some params for the crates -- local crateParams = { image = crateImageName, width = 50, height = 50, density = 5.0, friction = 0.3, bounce = 0.2 } -- Spawn crate 1 -- local crate = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate.x, crate.y = 300, 600 physics.addBody( crate, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) -- Setting up a health pool which can count down in the listener function. crate.healthPool = 500 -- Just a boolean value to see if the crate is already marked for removal. crate.garbage = false crate.bodyType = "static" crate.name = "Crate 01" crate:addEventListener( "particleCollision", objectParticleCollisionListener ) -- Spawn crate 2 -- local crate2 = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate2.x, crate2.y = 360, 800 physics.addBody( crate2, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) crate2.healthPool = 500 crate2.garbage = false crate2.bodyType = "static" crate2.name = "Crate 02" -- IMPORTANT -- If you uncomment the last line, the second crate receives its event listener. -- The app will crash, at least in my tests. --crate2:addEventListener( "particleCollision", objectParticleCollisionListener )

Hi @wabugi,

I did some testing, and this seems to be a bug related solely to removal of the object (this is where it crashes). However, this might be a “blessing in disguise” since, typically, it’s better to create physics objects once and recycle them throughout the game, versus creating and destroying and creating and destroying over and over. So, what you could do is just set the “killed” objects to sensors-type objects and make them invisible, then, if necessary, set them as inactive or move them off screen after a 10 millisecond timer. This works in my test, and you’ll get better performance this way.

As for the actual bug, we’re aware of some isolated bugs with particleCollision and we’re investigating the cause. But in the meantime, you could use the above approach for your game.

Hope this helps,

Brent

@Brent

I’m happy to hear that this is a bug and that I’m not completely dumb ;) 

Your solution is actually quite good. I already tried it and it works. I think it will be sufficient for my game. 

Thank you very much! :slight_smile:

generally you can remove the object after a 1>10 millisecond to second delay using a timer.performWithDelay, within your collision handler

Good point, but I already tried this in different ways and with different delays.

The Liquid Fun “particle rain” is more or less continuous in my scene. As long as the crate exists, there will always be particles colliding with it. 

When the “killing” particle hits the crate, I can of course set a delay to delete the crate. The killing collision event should be over then. But other particles will still be colliding with the crate when the final removal after the delay happens. 

I also tried to first remove the collision event listener, so the crate will not listen to particle collision anymore. But it seems like already started collision events will be calculated to their ended phase although the event listener is no longer existent. Please correct me if I’m wrong here because I just guess. 

We can look at the whole scene with just two particles to make it simpler. The first particle hits the crate and should destroy it. The second particle landed on the crate and is still there. Is it not possible to remove the crate as long as the second particle’s collision event is active?

Or is there maybe a way to cancel all active collision events of an object before removing it?

EDIT: 

According to this documentation:

http://docs.coronalabs.com/guide/physics/collisionDetection/index.html

I am allowed to call the object:removeSelf() method within a collision event. So why does the app crash then? 

Alright, I created a new simple test app. I added two crates (physics body, static) and a quite big rectangle of Liquid Fun particles falling down onto the crates right from the start. 

I’m removing the event listener of the crates during the collision event began phase and I also call the object:removeSelf() method immediately without any delay. It works! 

I got the feeling that there is something in my “old” code messing with the collision events at some point. From my current point of view I’m chasing a bug in my code and not a general problem with object removal during collision events. 

I’ll let you know when I got news.


UPDATE

Well it works as long as the crates are on the same y coordinates with the same removal point in time. When setting different removal times, the app gets stuck at the first crate removal. I’ll give it another try tomorrow. Maybe I just need a break.

I was able to get closer to the actual problem. First things first. 

When there is only one object in the scene with an event listener to the particleCollision, the colliding object can be removed during the collision event without any problems. Not even a delay is needed. Right before the removal I also remove the event listener, just to be sure the object does no longer collide. 

As soon as there is a second object in the scene also listening to the particleCollision, bad things begin to happen. It doesn’t even matter if the listener methods are the same or completely different ones. When the removal of both objects happens at nearly the same time (± 100 ms), the removal works but you still see the app stuttering at that point in time. When the gap between the removals becomes bigger, the app will surely crash. 

I have no great knowledge of Corona. Is it possible, that you can not remove an object colliding with a particle system, while other objects still collide with or listen to the same particle system? 

Would be great to know if this is a bug which will be fixed in the future or if my problem is not solvable at all. 

Hi @wabugi,

I’m curious to see your code and how you’ve set this all up. Can you post it, as “minimalist” as possible for examination? i.e. please remove it from any Composer/Storyboard framework if it’s done that way, remove any redundant code, etc.

Thanks,

Brent

Here is my sample code in a simple version. Don’t forget to change the image file names at the top. The code works as long as you uncomment the last line. 

I know there are maybe unneeded loops in my listener function. I just wanted to prevent some errors.

local physics = require "physics" physics.start() --------------------------------------- -- IMPORTANT: Set your image names here --------------------------------------- local particleImageName = "water-particle-4.png" local crateImageName = "crate.png" -- A table containing functions etc. Makes it easier to call them from anywhere. local t = {} -- Basic particle system setup -- local myParticleSystem = physics.newParticleSystem( { filename = particleImageName, radius = 8, imageRadius = 16 } ) -- Spawning a block of water particles -- local waterblock = myParticleSystem:createGroup( { flags = { "water", "fixtureContactListener" }, x = 300, y = 100, color = { 0, 0.5, 1, 1 }, halfWidth = 200, halfHeight = 400 } ) -- Listener function for the crates -- t.objectParticleCollisionListener = function ( event ) -- Make sure the object is still there. if ( event.object ~= nil ) then -- Store an object reference in a local variable. local target = event.object if ( event.phase == "began" ) then -- Just check if the object has a health pool at all. if target.healthPool then -- Substract 1 health for every collision as long as the health pool is bigger than 0. if target.healthPool \> 0 then target.healthPool = target.healthPool - 1 end -- The health pool is now 0 if target.healthPool == 0 then print( target.name .. " health is 0." ) -- Check if the object is already flagged for removal. if target.garbage ~= true then print( target.name .. " will be removed now." ) -- Remove the event listener from the object first, to prevent future collision events. target:removeEventListener( "particleCollision", t.objectParticleCollisionListener ) -- Flag the object for removal to prevent the function from trying to remove it more than once. target.garbage = true -- Finally remove the object from the scene. display.remove(target) -- Set the object to nil, just to be sure there is nothing left. target = nil end end end end end -- Return true in every case to complete the collision event return true end -- Some params for the crates -- local crateParams = { image = crateImageName, width = 50, height = 50, density = 5.0, friction = 0.3, bounce = 0.2 } -- Spawn crate 1 -- local crate = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate.x, crate.y = 300, 600 physics.addBody( crate, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) -- Setting up a health pool which can count down in the listener function. crate.healthPool = 500 -- Just a boolean value to see if the crate is already marked for removal. crate.garbage = false crate.bodyType = "static" crate.name = "Crate 01" crate:addEventListener( "particleCollision", t.objectParticleCollisionListener ) -- Spawn crate 2 -- local crate2 = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate2.x, crate2.y = 360, 800 physics.addBody( crate2, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) crate2.healthPool = 500 crate2.garbage = false crate2.bodyType = "static" crate2.name = "Crate 02" -- IMPORTANT -- If you uncomment the last line, the second crate receives its event listener. -- The app will crash, at least in my tests. --crate2:addEventListener( "particleCollision", t.objectParticleCollisionListener )

Hi @wabugi,

Can you try removing the function(s) from that “t” table? I know you’re trying to keep them organized, but this may be causing some issue with the removal of the listener and such. Please structure it just as a local function somewhere above, and see if you still get the crash.

Thanks,

Brent

Here you go. The behavior didn’t change. Still crashing when 2 objects are listening. 

local physics = require "physics" physics.start() --------------------------------------- -- IMPORTANT: Set your image names here --------------------------------------- local particleImageName = "water-particle-4.png" local crateImageName = "crate.png" -- Basic particle system setup -- local myParticleSystem = physics.newParticleSystem( { filename = particleImageName, radius = 8, imageRadius = 16 } ) -- Spawning a block of water particles -- local waterblock = myParticleSystem:createGroup( { flags = { "water", "fixtureContactListener" }, x = 300, y = 100, color = { 0, 0.5, 1, 1 }, halfWidth = 200, halfHeight = 400 } ) -- Listener function for the crates -- local function objectParticleCollisionListener( event) -- Make sure the object is still there. if ( event.object ~= nil ) then -- Store an object reference in a local variable. local target = event.object if ( event.phase == "began" ) then -- Just check if the object has a health pool at all. if target.healthPool then -- Substract 1 health for every collision as long as the health pool is bigger than 0. if target.healthPool \> 0 then target.healthPool = target.healthPool - 1 end -- The health pool is now 0 if target.healthPool == 0 then print( target.name .. " health is 0." ) -- Check if the object is already flagged for removal. if target.garbage ~= true then print( target.name .. " will be removed now." ) -- Remove the event listener from the object first, to prevent future collision events. target:removeEventListener( "particleCollision", objectParticleCollisionListener ) -- Flag the object for removal to prevent the function from trying to remove it more than once. target.garbage = true -- Finally remove the object from the scene. display.remove(target) -- Set the object to nil, just to be sure there is nothing left. target = nil end end end end end -- Return true in every case to complete the collision event return true end -- Some params for the crates -- local crateParams = { image = crateImageName, width = 50, height = 50, density = 5.0, friction = 0.3, bounce = 0.2 } -- Spawn crate 1 -- local crate = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate.x, crate.y = 300, 600 physics.addBody( crate, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) -- Setting up a health pool which can count down in the listener function. crate.healthPool = 500 -- Just a boolean value to see if the crate is already marked for removal. crate.garbage = false crate.bodyType = "static" crate.name = "Crate 01" crate:addEventListener( "particleCollision", objectParticleCollisionListener ) -- Spawn crate 2 -- local crate2 = display.newImageRect( crateParams.image, crateParams.width, crateParams.height ) crate2.x, crate2.y = 360, 800 physics.addBody( crate2, { density = crateParams.density, friction = crateParams.friction, bounce = crateParams.bounce } ) crate2.healthPool = 500 crate2.garbage = false crate2.bodyType = "static" crate2.name = "Crate 02" -- IMPORTANT -- If you uncomment the last line, the second crate receives its event listener. -- The app will crash, at least in my tests. --crate2:addEventListener( "particleCollision", objectParticleCollisionListener )

Hi @wabugi,

I did some testing, and this seems to be a bug related solely to removal of the object (this is where it crashes). However, this might be a “blessing in disguise” since, typically, it’s better to create physics objects once and recycle them throughout the game, versus creating and destroying and creating and destroying over and over. So, what you could do is just set the “killed” objects to sensors-type objects and make them invisible, then, if necessary, set them as inactive or move them off screen after a 10 millisecond timer. This works in my test, and you’ll get better performance this way.

As for the actual bug, we’re aware of some isolated bugs with particleCollision and we’re investigating the cause. But in the meantime, you could use the above approach for your game.

Hope this helps,

Brent