Spawning Objects

local W = display.contentWidth local H = display.contentHeight local physics = require("physics") physics.start() local circles = {} local circleGroup = display.newGroup() local function spawnCircle() local circle = display.newCircle( 100, 100, 30 ) circle:setFillColor(128,128,128) circle.x = math.random(0, W) circle.name = "circle" physics.addBody(circle, {bounce=0}) circle.bodyType = "kinematic" circle.isSensor = true circle.y = math.random(-100, -10) circle.index = #circles + 1 circleGroup:insert(circle) circle:setLinearVelocity(0, math.random(100, 110)) circles[circle.index] = circle return circle end local function removeCircle() for i, v in pairs(circles) do if circles[i].y \>240 then circles[i]:removeSelf() circles[i] = nil end end end Runtime:addEventListener("enterFrame", removeCircle) local SpawnTimer = timer.performWithDelay(1000, spawnCircle, 0)

Couple questions:

  1. Is it safe to use pairs()? I’ve tried a lua construct and it doesn’t appear to work. 

  2. Would this method properly remove the object itself and its table reference?

Thoughts?

Hi mort,

This will likely not be good for performance. The “pairs()” function is fine to use, but it’s not the fastest Lua function, and it should generally be avoided in time-critical situations or Runtime loops, which is exactly how you’re using it here. I don’t know how many circles you will have on screen at once, but that could be a lot of items to loop through every cycle of the game.

Since you’re using physics already, I suggest you just set up an invisible sensor region, and when the circles collide with that (or exit out one side of it on the “ended” phase), you remove them. Simple as pie, and much better for performance; no pairs() looping required. :slight_smile:

Take care,

Brent

Thanks for the prompt response, as usual!

You too!


I was having trouble accessing the table keys/references for nilling out after the collision.  (I’m only able to access the .name property of the collision object, and not its table key). Would you have any suggestions regarding that?

EDIT: I have discovered a way to nil all the keys of the spawn table onCollision, but I’m not sure if that’s the best approach.

Is there a more targeted way to approach this issue, say, only nilling the table key of an object after it has completed its collision (see issue above) ?

Also, I’ve tried using just a standard display group, and calling display.remove on the objects as they finish colliding with the barrier—that appears to work well too. I’m just concerned about the reduced flexibility of using standard display groups vs. the organizational benefits of tables, let me know if I’m missing out by only using groups. I’m still interested in solving my direct access to individual table keys onCollision issue though. 

=====================================

This is what I have based on your suggestions:

local W = display.contentWidth local H = display.contentHeight local physics = require("physics") physics.start() physics.setDrawMode( "hybrid" ) local circles = {} local circleGroup = display.newGroup() local onCollision local removeThreshold = display.newRect(0, H\*0.5, W, 50) physics.addBody( removeThreshold, { density = 10, friction = 0.5, bounce = 0 } ) removeThreshold.bodyType = "static" removeThreshold.isSensor = true removeThreshold.name = "threshold" removeThreshold.collision = onCollision removeThreshold:addEventListener( "collision", removeThreshold ) circleGroup:insert(removeThreshold) local function spawnCircle() local circle = display.newCircle( 100, 100, 30 ) circle:setFillColor(128,128,128) circle.x = math.random(0, W) circle.name = "circle" circle.collision = onCollision circle:addEventListener( "collision", circle ) physics.addBody(circle, {density=50, bounce=0, radius=30}) circle.bodyType = "dynamic" circle.y = math.random(-100, -10) circle.index = #circles + 1 circleGroup:insert(circle) circle:setLinearVelocity(0, math.random(1000, 1000)) circles[circle.index] = circle return circle end function onCollision( self, event ) if ( event.phase == "began" ) then elseif ( event.phase == "ended" ) then self:removeSelf() self= nil for i=1, #circles do circles[i] = nil end end end local function cleanAll() physics.stop() for i=1, #circles do circles[i]:removeSelf() circles[i] = nil end display.remove(circleGroup) circleGroup = nil end local SpawnTimer = timer.performWithDelay(500, spawnCircle, 0) local removeTimer = timer.performWithDelay(20000, function()timer.cancel(SpawnTimer); spawnTimer=nil;cleanAll();end,1) local monitorMem = function() collectgarbage() local memCount = collectgarbage("count") / 1024 if (prevMemCount ~= memCount) then print( "MemUsage: " .. memCount .. "MB") prevMemCount = memCount end local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000 if (prevTextMem ~= textMem) then prevTextMem = textMem print( "TexMem: " .. textMem ) end end Runtime:addEventListener( "enterFrame", monitorMem )

Possible solution:

function onCollision( self, event ) if ( event.phase == "began" ) then elseif ( event.phase == "ended" ) then if self.name == "circle" then for i=1, #circles do if circles[i] and circles[i].y \> removeThreshold.y then display.remove(circles[i]) circles[i] = nil self = nil -- is this necessary? end end end end end

Hi mort,

Is this new collision idea based on the circles colliding (well, ending collision) with an invisible sensor? If so, there’s no need to deal with all of that position checking. Just make it so the edge of the sensor is positioned such that when the circle exits it, it’s safe (visually) to remove it.

Brent

Yes. 

I’m actually using the position-checking as a means of determining which table references to remove after the collision has occurred. Using what was returned from the collision listener didn’t allow me access to the references. For example, I could remove the objects, but the table itself would keep growing, and so would memory usage. 

Question 1 : Which is the better approach?

A) Removing the objects and table references simultaneously (immediately after the collision)

B) Clearing the entire table later on, either entirely on scene exit OR periodically

         (the problem I see with periodically is that you may orphan active objects from their table references)

Question 2 : Regarding clearing out an object via a table, should I:

A) Iterate through the table, remove the objects, then nil out all the table references

B) Iterate through the table, remove the objects, then nil out all the table references, THEN nil out the object (is the last step here redundant?


EDIT:

I know your time is valuable, so I’ve refined my earlier post to be more concise. Thanks again!

Of course, I would prefer a simpler approach, whereby I could just easily remove the table reference along with the object through the self, or event.other; object1, object 2. This is the only way I can see to do a thorough clean up.  Please advise.

Hi mort,

I use a looping function to handle this kind of thing… it loops through whatever table you pass to it, until it finds a match, then it breaks the loop… that break being a little extra thing to improve performance, so it just stops dead after it finds a match and doesn’t keep looping through.

[lua]

local function removeID( tableID, name )

   local table_remove = table.remove

   for i=1,#tableID do

      local v = tableID[i] ; if ( v and v == name ) then table_remove( tableID,i ) ; break end ; v = nil

   end

   return false

end

[/lua]

And the usage would be something like:

[lua]

removeID( circles, self ) 

[/lua]

That should do it, but go ahead and test and see. :slight_smile:

Brent

Thanks!  It works for clearing out the table so, I assume, if I modify it as such:

local function removeID( tableID, name ) local table\_remove = table.remove for i=1,#tableID do local v = tableID[i] ; if ( v and v == name ) then table\_remove( tableID,i ) ; display.remove(name);name = nil; break; end ; v = nil end return false end

OR

local function removeID( tableID, name ) local table\_remove = table.remove for i=1,#tableID do local v = tableID[i] ; if ( v and v == name ) then display.remove(v); table\_remove( tableID,i ); break; end ; v = nil; name = nil; end return false end

it will be sufficient to properly remove the object involved in the collision?

Let me know if I have any redundant "nilling, and which of the above cases is better.

Okay. I think I see what was my confusion regarding “niling”.

They way I have it set up, whereby the object is returned into and set equal to a table [i], the object’s handle is actually its table index.

Thus,

local circle50 = display.newCircle…

is the same as

circles[50] = …

And, as long as I nil out the table key, I should be fine (as I’m actually nilling out the object as well)?

(Note: I realize if I remove an object with a standard handle first (not through its table reference), I must go back and clear it’s table reference later on.)

Hi mort,

If you add the circles to the “circles” table as simple items (not named key-value pairs), the loop I showed you will locate a match and remove it from the table.

The collision handling part is where you remove the display object and nil it out.

Right, that’s what I’m doing, I am calling the removeID(circles,self) within the collision handler. I’m just wondering if the method I used to combine the two would work as well?

EDIT: Whoops! I’m sorry, I forgot to include the collision handler, no wonder you didn’t see it!

local onCollision ... local function removeID( tableID, name ) local table\_remove = table.remove for i=1,#tableID do local v = tableID[i] if ( v and v == name ) then display.remove(v) v = nil table\_remove( tableID,i ) break end end return false end function onCollision( self, event ) if ( event.phase == "began" ) then elseif ( event.phase == "ended" ) then removeID( circles, self ) end end

Yep, that looks correct at a quick glance. :slight_smile:

You can test it by printing the # of items in the “circles” table before you call “removeID()”, then call it again after the removal (before the “return false”).

Brent

Thank you Brent! It appears you’ve addressed all my concerns. It helped being guided through the process, rather than just being given the answer—you’re a good teacher. 


Here is the final code, for future reference:

---Define variables: local W = display.contentWidth local H = display.contentHeight --Start physics: local physics = require("physics") physics.start() physics.setDrawMode( "hybrid" ) --Create table & group to hold circles/spawns: local circles = {} local circleGroup = display.newGroup() --Declare any forward references: local onCollision, spawnTimer, removeTimer --Create wall to trigger circle removal: local removeThreshold = display.newRect(0, H\*0.5, W, 50) physics.addBody( removeThreshold, { density = 10, friction = 0.5, bounce = 0 } ) removeThreshold.bodyType = "static" removeThreshold.isSensor = true removeThreshold.name = "threshold" removeThreshold.collision = onCollision removeThreshold:addEventListener( "collision", removeThreshold ) circleGroup:insert(removeThreshold) --Function for spawning circles: local function spawnCircle() local circle = display.newCircle( 100, 100, 30 ) circle:setFillColor(128,128,128) circle.x = math.random(0, W) circle.name = "circle" circle.collision = onCollision circle:addEventListener( "collision", circle ) physics.addBody(circle, {density=50, bounce=0, radius=30}) circle.bodyType = "dynamic" circle.y = math.random(-100, -10) circle.index = #circles + 1 circleGroup:insert(circle) circle:setLinearVelocity(0, math.random(1000, 1000)) circles[circle.index] = circle return circle end --Function for removing collided circles and cleaning their table indices: local function removeCircles( tableID, name ) local table\_remove = table.remove for i=1,#tableID do local v = tableID[i] if ( v and v == name ) then display.remove(v) v = nil table\_remove( tableID,i ) break end end return false end --Function for handling a collision: function onCollision( self, event ) if ( event.phase == "began" ) then print (self.name, event.other.name) elseif ( event.phase == "ended" ) then removeCircles( circles, self ) end end --Stop physics and timers and clean (all objects and tables): local function cleanAll() timer.cancel(spawnTimer) spawnTimer=nil physics.stop() for i=1, #circles do display.remove(circles[i]) circles[i] = nil table.remove( circles,i ) end display.remove(circleGroup) circleGroup = nil end --Spawning and Removal Timers: spawnTimer = timer.performWithDelay(500, spawnCircle, 0) removeTimer = timer.performWithDelay(10000, cleanAll,1) ----------------- --Monitor Memory: local monitorMem = function() collectgarbage() local memCount = collectgarbage("count") / 1024 if (prevMemCount ~= memCount) then print( "MemUsage: " .. memCount .. "MB") prevMemCount = memCount end local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000 if (prevTextMem ~= textMem) then prevTextMem = textMem print( "TexMem: " .. textMem ) end end Runtime:addEventListener( "enterFrame", monitorMem )

One final, related question:

If an object has a property attached to it, must it be nilled separately from the object itself when removing said object? 

i.e. 

… circle.isActive = true

Properties associated with the object are removed along with the object. Nice and simple. :slight_smile:

Great! Even if you attach a timer (or transition) to an object? 

circle.someTimer = timer.performWithDelay… timer.cancel(circle.someTimer) circle.someTimer = nil

or

Added to the example above your post previous to this one:

--Function for spawning circles: local function spawnCircle() local circle = display.newCircle( 100, 100, 30 ) … circle.someTimer = timer.performWithDelay(250, function() print("heh"); circle.xScale = 6; end,0) circles[circle.index] = circle return circle end ---LATER ON: --Function for removing collided circles and cleaning their table indices: local function removeCircles( tableID, name ) local table\_remove = table.remove for i=1,#tableID do local v = tableID[i] if ( v and v == name ) then timer.cancel(v.someTimer) v.someTimer = nil display.remove(v) v = nil table\_remove( tableID,i ) break end end return false end

Hi mort,

It’s good practice to cancel and nil out timers and transitions when you remove the object. You can do this, however, in the collision function if you want… that way, the “removeID” function can remain very general, and you don’t need to anticipate (or conditionally check) if the object has a timer or transition attached to it in that function. Although, it doesn’t hurt to conditionally check that these exist in the collision function, so you don’t get some “timer doesn’t exist” type of error.

Brent

Makes sense. I’ll try it both ways. 

For the time being, I’ve added a conditional check to my removeCircles function as follows:

 if v.someTimer then timer.cancel(v.someTimer) v.someTimer = nil end