Slowness as game level progresses

Hi there,
 
I have surfed the boards for two months now, yet this is my first post.
 
I have a problem which I hope is easy to spot:
 

  1. I have a game which creates multiple instances of solid color circles. Now the game starts OK and performance is acceptable on iPhone5 but as I go through the level I noticed a bit of a slow down in spawning and circles becomes less sensitive to touch (So to collide with each other). I don’t think it is a memory leak issue since if I go out of the composer scene i.e restart the level performance is back again. Could it be that I am creating too many circles with physics/touch etc. If so I am removing objects and nii them on collision; so effectively they are at any given time only 20. Am I correct?

Note in a single level around 500-2000 of these circles are created and removed but as said before only 20 remain on screen and not removed at any point in the game.

  1. Should I also remove the circles from table as well?

  2. Am I effectively removing the timer using  timerid_drawCircle = nil in that way (Since the objects/circles are spawned)?
    Also do I really have to remove the timer when it only loops once and will effectively cancel it self.

Here is my code.

Thanks in advance.

-- Draw Circle local function drawCircle(xPos, yPos, radius, cirName, cirDelay) local timerid\_drawCircle = timer.performWithDelay(cirDelay, function() local randcol = math.random( 1, 10 ) local r = colorTable[randcol][1] local g = colorTable[randcol][2] local b = colorTable[randcol][3] local myCircle = display.newCircle( xPos, yPos, radius ) myCircle:setFillColor( r/255, g/255, b/255 ) myCircle.myName = cirName myCircle.myColor = r..","..g..","..b sceneGroup:insert( myCircle ) --So to be removed when scene is changed --Insert circle into table circleTable[#circleTable+1] = myCircle --This is faster than one below --table.insert(circleTable, myCircle) physics.addBody( myCircle, { radius=cirRadius, density=3.0, friction=0.5, bounce=0.3 } ) myCircle.gravityScale = 0 -- Make the myCircle instance respond to touch events myCircle:addEventListener( "touch", onTouch\_OneDir ) -- Detect pre collisions --myCircle.preCollision = onPreCollision --myCircle:addEventListener( "preCollision", myCircle ) -- Detect collisions myCircle.collision = onLocalCollision myCircle:addEventListener( "collision", myCircle ) end, 1) timerid\_drawCircle = nil end --Now we will loop the below 20x initially to create 20 circles for i = 1, 20, 1 do circleRGB(xPos, yPos, cirRadius, cirName, math.random( 0, 2000 )) end --Then when each two circles collide another two are created in place using the same code

Hi @support271,

You should definitely create a method/routine where you pre-create approximately the most number of circles you’ll need (20 or 30) and establish them as physics objects, then keep them somewhere offscreen, inactive, with no collision listeners on them. Then, as needed, give them the proper listeners (touch, collision, etc.) and place them onscreen. Then, when you need to “remove” them, just put them back offscreen, inactive once again. This kind of setup is ultimately better for performance, since you’re creating a re-useable cache of objects that can be swapped in an out as required.

Hope this helps,

Brent

Hi Brent,

Thanks for the reply. I have actually though of that but wouldn’t it be the same since at one time the same number of touch, collision and physics objects will be on screen? Plz explain this more to me. Also I will have to create 20 circles * 10 colors i.e 200, because the user might want the same color circle 20 times on screen. Isn’t this a high amount of objects to create on screen even if inactive?

I agree with Brent. Create 20 to 25 circles and always reuse these. Keep them offscreen when “dead” and change their colors and put them on-screen again when they’re supposed to spawn. No need at all to destroy and create instances on the fly constantly - and without looking at your code: your slowdown is probably due to bad cleanup code, and this approach will easily fix that.

Thanks Thomas,

Will do that; I had this idea but was hesitant to do it. Since major and I am talking major recoding is needed, as this is connected to almost everything in the game. But I agree it is the correct thing to do.

should I move them using x, y or transition. I heard transitions is faster but can cause memory leak if not canceled properly.

What about questions 2 and 3 can you help me with that Brent ant Thomas.

Thanks

you could speed up your game massively if you localised all of the globals that you call regularly eg

local timer\_performwithdelay = timer.performWithDelay local display\_newCircle = display.newCircle -- etc .....

Hi,

Can you briefly describe how the game works? I mean, how you play it? That will help me to see how you should structure it.

Regarding questions 2 and 3: I believe that the best way is probably to create a table at start of the game (or the level) that holds 20 circles. Regarding niling the timer: tricky question, I always forget about stuff like this, because to be honest, it won’t create any memory leak that you would notice, if you go with the “table with 20 circles” approach.

Since the value is a local, it would eventually get garbage-collected, but explicitly setting it to nil frees up the memory faster, so I’d keep the nil part.

#86lemonade68

I try my best to make most things local and almost all my code is local expect for a few things here and there. The circle and timer are already local as you can see, but the listeners (collision & touch) and such are not local because I remember they caused issues for me so I made them global. Probably a scoping issue so I will investigate further and make them local. Thanks

#thomas6

I will do as you say in pre-creating 20 or so circles; but wait until version 2 of the game; as it seems the best approach. I cannot risk doing it at this stage just prior to launch. The performance slowing is not that noticeable on short lengthened levels, so for now I will remove the lengthy levels and reincorporate them when I optimize the code further as you and Brent suggested.

  1. What I meant is are   object:removeSelf()  and object = nil   enough to completely delete object from memory or would I have to delete reference from table as well too free it. As you can see from my code above I use circleTable[#circleTable+1] = myCircle  to add to table does that prevent object from being properly removed.

  2. But would the nil as I used it work because I heard with spawning you risk only removing the last timer for last object only. Am I doing it correctly.

I have done many complex things in the game but yet these small issues troubles me :slight_smile:

Brent and Rob please feel free to share your comments on my last post. Thomas has been an incredible help but I like to hear more than one opinion before butchering my game open  and recoding like 50% of it :slight_smile:

You need to remove all references to the circle for the memory to be cleared. If you don’t, you’re not really removing the object, just it’s display part.
[lua]
local myObj = display.newImageRect(“myImage.png”, 50, 50)
objects[#objects + 1] = myObj

myObj:removeSelf()
myObj = nil
[/lua]
At the last line, you’re removing the “myObj” reference to the object, but you’re not taking away the final reference (objects[#objects + 1]), so the Lua garbage collector doesn’t know to remove it. You don’t actually need to nil out local references (they nil out automatically), but table references must be cleared.
[lua]
myObj:removeSelf()
objects[#objects] = nil – No need for “myObj = nil” because “myObj” is a local reference
[/lua]

  • C

But objects[#objects] = nil would only nil the last element in table not the specific one? Wouldn’t you need to find the element in the table then nil it?

Yes, you’d need to remove the correct object from the table. Using table.indexOf might be too slow, so you can try making special “circle reference” tables, like so:
[lua]
{circle = circle, shouldBeKilled = false}
[/lua]
and add those to the list of circles instead,. When you delete a circle, set it’s shouldBeKilled to false. Iterate once every few frames to remove the circles that have shouldBeKilled = true, and you’ll be good.

Of course, now you need to know which element the circle points to, but that’s easy:
[lua]
circle.circleEntry = [the circle entry in the table]
[/lua]
Because of Lua’s reference system, if you set ‘circle.circleEntry.shouldBeKilled’ to true, you’ll also set the version in the circles table, and it’ll work nicely.

  • C

another approach is to abandon numeric indexes, and key your “object list table” with object references, fe:

local aDisplayObject = display.newCircle(…

myObjectList = {}

myObjectList[aDisplayObject] = aDisplayObject – the index to the object *IS* the object

to remove an object:

myObjectList[aDisplayObject] = nil – remove reference from table, don’t need to “indexOf” nuthin!

display.remove(aDisplayObject] – remove object from display

aDisplayObject = nil – remove any other references still lingering

this will also keep your list from becoming “sparse” as will happen with numeric indices if you nil entries from the middle.  you don’t want to be iterating over a list that eventually grows to 2000 items long to find just the 20 with actual values.  (table.remove can also prevent that, but has its own cost associated with shuffling the list)

to iterate your list you’ll need to use pairs() – this is the tradeoff, a bit slower iteration but far faster removal/cleanup.  (tho with only 20 items to iterate, doubt it makes any practical difference)  but if you currently have a large sparse list, then pairs() on a small dense list might actually *improve* your performance.

hth

Thanks dave for the tip it is great and easier to manage than using an index.

I’m becoming curious now :slight_smile: So how does the game work?

#Thomas

lol I tried my best to avoid answering that question.

When its ready (a couple of weeks) you will be the first to know, promise :slight_smile:

Be aware that davebollinger’s approach works fine, but you probably don’t want to use that if you get a bunch of objects. Using tables as keys is slightly slower than numbers or strings (don’t ask me why, I’m just going by timing both).

Of course, the difference is negligible for a few objects, but if you start to get bigger amounts (100+), you’ll want to switch.

  • C

Yes I am aware of this, but its just 20 objects, I dont’ think it will be a problem. It would be an over kill if I am going to use it for the 500-2000 objects.

Thanks for the heads up.

right, my suggestion predicated on the OP’s “at any given time only 20”

(and… assuming OP still prefers to avoid the “reuse a constant pool of 20” approach as suggested by others)

Thanks all.

It was a pleasure having people answer you so quickly, this rarely happens on forums nowadays. Its a very vibrant community, I love that about corona.

I actually finished most of the game; it took me 2 months only, which is super fast for me, coming form a web development and desktop application background. Corona has been a great journey to me and I learned a lot. I have learned and used things like physics, collisions, touch and dragging, animation, file manipulation, SQLite Integration, In App Purchases, Social Sharing, composer, audio playback, webview, tableview, scroll view, email and sms sending all from reading the detailed documentation and the forums.

I was hesitant to ask on the forum to be honest (My pride I guess) since I am a know it all type of guy, or I think I am. I fixed all my issue by reading the forums and stackoverflow, but I wanted to make sure of this before I launch the game. Glad I did.

I will also register as paid member now.