Collision manager, problems

I’m working on a game. I have a module I’ve named collision_manager. This modules stores objects in two arrays: bullets and enemies. I check for collisions between objects by looping though bullets with pairs(). With each bullet in the array I loop through all of the enemies in the enemy array and check for a collision between that bullet and each enemy. 

This all works well. Then a problem occurs at some point where I get the following error:

ERROR: Attempt to remove an object that’s already been removed from the stage or whose parent/ancestor group has already been removed.

This can happen soon, or take a while, but always happens eventually. Obviously I’ve removed something and then tried to remove it again. I’m using a key type array to add each item to the array:

bullet_array[bullet] = bullet

And then removing items like this:

bullet_array[bullet] = nil

Seems like this should work, and it does work, for a while. I’m having trouble figuring out where it goes wrong. Any suggestions, I’m open to new ideas on how this might be improved. 

Here’s a sample of the module code: 

--------------------------------------------------- -- Collision Manager --------------------------------------------------- local M = {} local enemy\_array = {} local bullet\_array = {} -------------------------------------------------- local function hit\_test\_point( point, rect ) if point.x \> rect.l and point.x \< rect.r and point.y \> rect.t and point.y \< rect.b then return true else return false end end -------------------------------------------------- --------------------------------------------------- local function add\_enemy( enemy ) enemy\_array[enemy] = enemy end M.add\_enemy = add\_enemy --------------------------------------------------- local function add\_bullet( bullet ) bullet\_array[bullet] = bullet end M.add\_bullet = add\_bullet --------------------------------------------------- local function remove\_enemy( enemy ) enemy\_array[enemy] = nil end M.remove\_enemy = remove\_enemy --------------------------------------------------- local function remove\_bullet( bullet ) bullet\_array[bullet] = nil end M.remove\_bullet = remove\_bullet -------------------------------------------------- local function check\_enemies( bullet ) for enemy\_k, enemy in pairs( enemy\_array ) do if enemy ~= nil then local point = {x=bullet.x, y=bullet.y} local rect = {l=enemy.contentBounds.xMin, t=enemy.contentBounds.yMin, r=enemy.contentBounds.xMax, b=enemy.contentBounds.yMax} if hit\_test\_point( point, rect ) then remove\_bullet( bullet ) remove\_enemy( enemy ) bullet.remove\_bullet( bullet ) enemy.remove\_enemy( enemy ) end end end end local function check\_bullets() for bullet\_k, bullet in pairs( bullet\_array ) do if bullet.x ~= nil then check\_enemies( bullet ) end end end local function frame\_update() check\_bullets() end M.frame\_update = frame\_update --------------------------------------------------- return M

I starting to think my problem has something to do with transition. It seems that the error occurs when a transition completes but the object has already been removed. I tried to cancel the transition. I got no errors canceling the transitions but the onComplete still seem to fire…

Hi @soggybag,

Can you confirm once again that you’re successfully cancelling the transitions? I often find it best to attach transitions to the actual objects, like:

[lua]

myObject.trans = transition.to( … )

[/lua]

Then, when you remove the object, it’s easy to check:

  1. Does the transition exist, as in “if (myObject.trans) then … end” ?

  2. If so, cancel the transition and set “myObject.trans” to nil

If you do this and the transition onComplete is still firing, let me know.

Thanks,

Brent

Thanks Brent, I think you were correct. I have been saving transitions in an array. Imagine an object has more than one transition, it might move down then across, then down again. If one or more of the transitions has completed before the object is removed, trying to cancel all the transitions in the array seems to generate an error. 

I’m guessing I should probably check the transition reference before removing the transition. What exactly is returned from transition.to()? 

Is there a better way to handle this type of situation? 

Hi @soggybag,

Perhaps I’m mistaken, but if you’re running multiple transitions at once (which isn’t common, but sometimes it’s necessary), shouldn’t the “onComplete” be executed only on the last one? But if I get what you’re doing, you add transitions along the way, and maybe a previous one isn’t the last anymore. Maybe you could implement some kind of “isLastTrans” property on the object, set to the most recently-called transition, and then in the onComplete handler, you detect if that transition is the final one. If so, then (and only then) do you remove the object.

Thanks again for the reply Brent. 

Let me outline a scenario. I have an object that wants to move down, then left then down again. I have been implementing this with three transitions, the second two use a delay.

I’m definitely only using the onComplete on the last transition. 

I notice if I trace the reference to the transition I see a table in the terminal. What is this exactly? Does this table contain any useful properties or methods? Might be nice if this table had it’s own cancel method. Then you could cancel a transition with something like:

local t = transition.to( object, {} ) t:cancel()

That would be neat and tidy. 

Hi @soggybag,

This FAQ might explain it better:

http://www.coronalabs.com/blog/2012/08/15/faq-wednesday-display-object-listeners/

Notice that if you follow the method described, the “target” parameter of the onComplete (or “event” as many people use) is a reference to the object that was undergoing transition. So, if you put the transition as a property of the object, as in “object.trans = transition.to( … )”, you can access the transition itself by saying “target.trans” in the onComplete.

If you want to check the “contents” of the table you’re getting for the transition, you can do a Lua pairs loop on it. Using this, I see the following elements:

[lua]

2013-06-12 16:01:49.607 Corona Simulator[50505:707] _transition    function: 0x12f701150

2013-06-12 16:01:49.607 Corona Simulator[50505:707] _keysStart    table: 0x12f7c9930

2013-06-12 16:01:49.607 Corona Simulator[50505:707] _timeStart    14.416

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _onComplete    function: 0x12f7c97e0

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _keysFinish    table: 0x12f7c9970

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _target    table: 0x12f7c9720

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _duration    2000

[/lua]

Hope this helps!

Brent

I starting to think my problem has something to do with transition. It seems that the error occurs when a transition completes but the object has already been removed. I tried to cancel the transition. I got no errors canceling the transitions but the onComplete still seem to fire…

Hi @soggybag,

Can you confirm once again that you’re successfully cancelling the transitions? I often find it best to attach transitions to the actual objects, like:

[lua]

myObject.trans = transition.to( … )

[/lua]

Then, when you remove the object, it’s easy to check:

  1. Does the transition exist, as in “if (myObject.trans) then … end” ?

  2. If so, cancel the transition and set “myObject.trans” to nil

If you do this and the transition onComplete is still firing, let me know.

Thanks,

Brent

Thanks Brent, I think you were correct. I have been saving transitions in an array. Imagine an object has more than one transition, it might move down then across, then down again. If one or more of the transitions has completed before the object is removed, trying to cancel all the transitions in the array seems to generate an error. 

I’m guessing I should probably check the transition reference before removing the transition. What exactly is returned from transition.to()? 

Is there a better way to handle this type of situation? 

Hi @soggybag,

Perhaps I’m mistaken, but if you’re running multiple transitions at once (which isn’t common, but sometimes it’s necessary), shouldn’t the “onComplete” be executed only on the last one? But if I get what you’re doing, you add transitions along the way, and maybe a previous one isn’t the last anymore. Maybe you could implement some kind of “isLastTrans” property on the object, set to the most recently-called transition, and then in the onComplete handler, you detect if that transition is the final one. If so, then (and only then) do you remove the object.

Thanks again for the reply Brent. 

Let me outline a scenario. I have an object that wants to move down, then left then down again. I have been implementing this with three transitions, the second two use a delay.

I’m definitely only using the onComplete on the last transition. 

I notice if I trace the reference to the transition I see a table in the terminal. What is this exactly? Does this table contain any useful properties or methods? Might be nice if this table had it’s own cancel method. Then you could cancel a transition with something like:

local t = transition.to( object, {} ) t:cancel()

That would be neat and tidy. 

Hi @soggybag,

This FAQ might explain it better:

http://www.coronalabs.com/blog/2012/08/15/faq-wednesday-display-object-listeners/

Notice that if you follow the method described, the “target” parameter of the onComplete (or “event” as many people use) is a reference to the object that was undergoing transition. So, if you put the transition as a property of the object, as in “object.trans = transition.to( … )”, you can access the transition itself by saying “target.trans” in the onComplete.

If you want to check the “contents” of the table you’re getting for the transition, you can do a Lua pairs loop on it. Using this, I see the following elements:

[lua]

2013-06-12 16:01:49.607 Corona Simulator[50505:707] _transition    function: 0x12f701150

2013-06-12 16:01:49.607 Corona Simulator[50505:707] _keysStart    table: 0x12f7c9930

2013-06-12 16:01:49.607 Corona Simulator[50505:707] _timeStart    14.416

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _onComplete    function: 0x12f7c97e0

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _keysFinish    table: 0x12f7c9970

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _target    table: 0x12f7c9720

2013-06-12 16:01:49.608 Corona Simulator[50505:707] _duration    2000

[/lua]

Hope this helps!

Brent