Is there any reason why this block of code alone would slow my game to a crawl?

Hi, I’m trying to add a sort of magnetic attraction effect to my game by using rings shrinking towards a centre point. Without this code my game runs at a very solid frame rate on my HTC One X (hovers around 60 fps). When I add the code, it drops to about 12 fps. Any ideas? Thanks for looking.

local mRand = math.random local rings = display.newGroup() local spawnRing = function(params) local ring = display.newCircle(params.group, \_W\*0.5 + 100, \_H\*0.5, params.radius) ring : setFillColor(0, 0, 0, 0) ring.strokeWidth = params.thickness ring : setStrokeColor(255, 255, 255) ring.radius = params.radius ring.reachedCentre = false ring.alpha = 0.5 function ring : destroy() display.remove(self) self = nil end return ring end for i = 1, 8 do spawnRing({radius = i\*44 + mRand(-5, 5), thickness = mRand(1, 6), group = rings}) end local shrinkRings = function() for i = 1, rings.numChildren do if rings[i] then rings[i] : scale(0.99, 0.99) rings[i].radius = rings[i].radius\*0.99 if rings[i].radius \<=100 and rings[i].reachedCentre == false then rings[i].reachedCentre = true spawnRing({radius = 356, thickness = mRand(1, 6), group = rings}) elseif rings[i].radius \<=50 then rings[i] : destroy() end end end end Runtime : addEventListener("enterFrame", shrinkRings)

Hey,

I’m also having a similar problem in a game that I am working on, in that I have a lot going on in my “game loop”. You have the shrinkRings() function that will be getting called 60 times per second (or at least your mobile device is attempting to do that!).  And in that 60 times per second it will be performing numerous operations on existing objects (calculating radius, calling math.rand, etc…).  

I believe that what is grinding your game to a halt though is likely to be all the display objects that are being spawned during this time.  Can you try to create a pool of these rings on “createScene” or “willEnterScene” (basically, not on the fly) and then just “activate” them (isBodyActive=true, isVisible=true, set radius, etc…) when you actually require them to be active?

Rich

Adding/removing table items in a game loop is a time-exhaustive process…

Couple of things that may help…

  1. Do your loop backwards [lua] for i = rings.numChildren, 1, -1 do [/lua], especially if you are removing items from an array.

  2. Don’t remove items from the array in your game loop.  I generally create items before entering the game loop and mark “dead” items (something like [lua] rings[i].isAlive = false [/lua], and then clean them up later.  Might not always be possible to spawn the correct amount needed in your game, but spawn as many or a few more than you think you need.

Thanks for the responses. I’ve changed a lot of the code. I am now only checking the smallest ring radius on “enterFrame”. I had another version with no runtime event listeners, but I was having trouble spawning a new ring the moment the smallest one got to the centre. 

local indexToCheck = 1 local checkSmallestRingRadius = function() if indexToCheck \<= rings.numChildren and rings[indexToCheck].radius \<= 100 then rings[indexToCheck].isAlive = false rings[indexToCheck].isVisible = false local outerRing = spawnRing({radius = 356, thickness = mRand(1, 6), group = rings}) outerRing.tween = transition.to(outerRing, {time = outerRing.tweenTime, tweenTime = 0, alpha = 0, xScale = 0.001, yScale = 0.001, radius = 0}) indexToCheck = indexToCheck + 1 end end local shrinkRings = function() rings.isVisible = true for i = 1, rings.numChildren do rings[i].tween = transition.to(rings[i], {time = rings[i].tweenTime, tweenTime = 0, alpha = 0, xScale = 0.001, yScale = 0.001, radius = 0}) end end local stopShrinkingRings = function() rings.isVisible = false for i = 1, rings.numChildren do transition.cancel(rings[i].tween) end end local destroyRings = function() for i = rings.numChildren, 1, -1 do if not rings[i].isAlive then rings[i] : destroy() end end end

checkSmallestRingRadius is a runtime event listener that is added and removed with a button press. On release stopShrinkingRings and then destroyRings are called. This is a little better. It only slows down to about 20fps. I’d like to get it up to at least 30fps. I use delta time for the rest of the movement in the game so I hope the slowdown will be only minimally noticeable at most. Is there anything else I can do?

I think that the spawning and destroying of objects is where your slowdown occurs.

If it is possible, I would not destroy any object until the level is over… if you are using storyboard, perhaps put that in the exitScene() function. 

Same with spawning… if possible create a bunch of objects before the level starts and use/reuse them when spawning is needed. 

In a game I am working on I create many objects and store them in a table.  When they “die”, they are marked as isAlive = false, and I store the table index in another table, so that when I need to create another object, I can reuse the dead one.  In cases where I do not have any dead objects to reuse, I will create another single object.  I don’t really notice any slowdown using that method.

I did run into the same issue when I was creating/killing objects during gameplay.

Hope this helps.

OK, I solved it for the most part. I keep track of each rings original radius so I can scale it back up when it reaches the centre. Only 8 rings and no spawning or destroying. I could even change the strokewidth when I scale them back up to make it look like a new ring each time, but this looks fine and runs between 30 and 40 fps. dt is delta time http://www.coronalabs.com/blog/2013/06/18/guest-tutorial-delta-time-in-corona/

local shrinkRings = function(dt) for i = 1, rings.numChildren do local scale = 1 - 0.01\*dt local alphaScale = 1 - 0.005\*dt rings[i] : scale(scale, scale) rings[i].radius = rings[i].radius \* scale rings[i].alpha = rings[i].alpha \* alphaScale if rings[i].radius \<= 100 then rings[i].xScale = 330 / rings[i].originalRadius rings[i].yScale = 330 / rings[i].originalRadius rings[i].radius = 330 rings[i].alpha = 0.5 end end end

Thanks for the help, guys.

Hey,

I’m also having a similar problem in a game that I am working on, in that I have a lot going on in my “game loop”. You have the shrinkRings() function that will be getting called 60 times per second (or at least your mobile device is attempting to do that!).  And in that 60 times per second it will be performing numerous operations on existing objects (calculating radius, calling math.rand, etc…).  

I believe that what is grinding your game to a halt though is likely to be all the display objects that are being spawned during this time.  Can you try to create a pool of these rings on “createScene” or “willEnterScene” (basically, not on the fly) and then just “activate” them (isBodyActive=true, isVisible=true, set radius, etc…) when you actually require them to be active?

Rich

Adding/removing table items in a game loop is a time-exhaustive process…

Couple of things that may help…

  1. Do your loop backwards [lua] for i = rings.numChildren, 1, -1 do [/lua], especially if you are removing items from an array.

  2. Don’t remove items from the array in your game loop.  I generally create items before entering the game loop and mark “dead” items (something like [lua] rings[i].isAlive = false [/lua], and then clean them up later.  Might not always be possible to spawn the correct amount needed in your game, but spawn as many or a few more than you think you need.

Thanks for the responses. I’ve changed a lot of the code. I am now only checking the smallest ring radius on “enterFrame”. I had another version with no runtime event listeners, but I was having trouble spawning a new ring the moment the smallest one got to the centre. 

local indexToCheck = 1 local checkSmallestRingRadius = function() if indexToCheck \<= rings.numChildren and rings[indexToCheck].radius \<= 100 then rings[indexToCheck].isAlive = false rings[indexToCheck].isVisible = false local outerRing = spawnRing({radius = 356, thickness = mRand(1, 6), group = rings}) outerRing.tween = transition.to(outerRing, {time = outerRing.tweenTime, tweenTime = 0, alpha = 0, xScale = 0.001, yScale = 0.001, radius = 0}) indexToCheck = indexToCheck + 1 end end local shrinkRings = function() rings.isVisible = true for i = 1, rings.numChildren do rings[i].tween = transition.to(rings[i], {time = rings[i].tweenTime, tweenTime = 0, alpha = 0, xScale = 0.001, yScale = 0.001, radius = 0}) end end local stopShrinkingRings = function() rings.isVisible = false for i = 1, rings.numChildren do transition.cancel(rings[i].tween) end end local destroyRings = function() for i = rings.numChildren, 1, -1 do if not rings[i].isAlive then rings[i] : destroy() end end end

checkSmallestRingRadius is a runtime event listener that is added and removed with a button press. On release stopShrinkingRings and then destroyRings are called. This is a little better. It only slows down to about 20fps. I’d like to get it up to at least 30fps. I use delta time for the rest of the movement in the game so I hope the slowdown will be only minimally noticeable at most. Is there anything else I can do?

I think that the spawning and destroying of objects is where your slowdown occurs.

If it is possible, I would not destroy any object until the level is over… if you are using storyboard, perhaps put that in the exitScene() function. 

Same with spawning… if possible create a bunch of objects before the level starts and use/reuse them when spawning is needed. 

In a game I am working on I create many objects and store them in a table.  When they “die”, they are marked as isAlive = false, and I store the table index in another table, so that when I need to create another object, I can reuse the dead one.  In cases where I do not have any dead objects to reuse, I will create another single object.  I don’t really notice any slowdown using that method.

I did run into the same issue when I was creating/killing objects during gameplay.

Hope this helps.

OK, I solved it for the most part. I keep track of each rings original radius so I can scale it back up when it reaches the centre. Only 8 rings and no spawning or destroying. I could even change the strokewidth when I scale them back up to make it look like a new ring each time, but this looks fine and runs between 30 and 40 fps. dt is delta time http://www.coronalabs.com/blog/2013/06/18/guest-tutorial-delta-time-in-corona/

local shrinkRings = function(dt) for i = 1, rings.numChildren do local scale = 1 - 0.01\*dt local alphaScale = 1 - 0.005\*dt rings[i] : scale(scale, scale) rings[i].radius = rings[i].radius \* scale rings[i].alpha = rings[i].alpha \* alphaScale if rings[i].radius \<= 100 then rings[i].xScale = 330 / rings[i].originalRadius rings[i].yScale = 330 / rings[i].originalRadius rings[i].radius = 330 rings[i].alpha = 0.5 end end end

Thanks for the help, guys.