Performance tips (get your game to run at 60fps!)

Hi all. We’ve been asked by a few developers how we got our game, GoNinja, to run as smoothly as it does. I wrote a small list in response to a question on a blog post, and I’m reposting it here since I think many of you might find it useful.

  1. Preload all images before the start of the gameplay and don’t load any more during it. We keep everything invisible until we need to place it on the screen, and once it moves off the screen we make it invisible again.

  2. Corona sqlite database writes are synchronous and slow. If you need to write to the database, batch up as much as you can into one query and avoid making too many queries at all costs. On slower devices the game lags a bit every time you kill a guy since we need to write data for achievements (just one small database query), no way to fix this completely, but it could be a lot worse.

  3. Avoid accessing or changing the .x and .y properties of display objects or display groups as much as possible. We only check these once per frame per object and then we cache them so they don’t need to be read again. A lot of people don’t realize that just calling “image.x” too many times can actually be really slow.

  4. To reduce memory usage, use smaller versions of some sprites and scale them up. When things are going by fast, people won’t notice a small reduction in image quality on some areas. Keep the detail high on the things that matter.

  5. Do not place functions in the global scope. If you have code anywhere that looks like “function doSomething()…” instead of “local function doSomething()…” then you may be unnecessarily using extra memory between scenes. Images that are used inside of these global functions will not be garbage collected on a scene change.

  6. On some android devices you might need to go down to 30fps. Single core devices in particular start having audio issues and other problems if you push them too hard. We ended up defaulting to 30FPS for all android devices, but we might bring it up to 60 for the more powerful ones in the future. [import]uid: 135827 topic_id: 27179 reply_id: 327179[/import]

Great tips, glad that you shared this! The game looks real smooth and professional, well done!

Regarding no 3. I have built a animation engine and I can’t see any impact on x and y, even scale and alpha is involved. I am doing at most over 125 changes every frame to my objects and don’t see any lag. At most I guess I am doing 3750 changes every second and still is my animation running smooth. I am running my app at 60fps but forced down the enter frame event down to 30 fps, I am not sure if this has an effect? I am accessing all display objects properties with transition.to with one call, could that be more efficient, than accessing them line by line?

--Is this faster?  
transition.to(myObj,{x=arr[0].x, y=arr[0].y, time=1})  
  
--then this?  
myObj.x = arr[0].x;  
myObj.y = arr[0].y;  
  

How did you came up with this conclusion that x and y would impact on the performance?

Regarding Sqlite, I agree, I had terrible performance issue and you should stay away from it in game mode.

Joakim [import]uid: 81188 topic_id: 27179 reply_id: 110356[/import]

Thanks jkrassman!

Calling or changing x and y too much can definitely hurt performance. But of course it only starts to matter once a frame takes longer than 1/60th of a second to process. It sounds like in your case that’s what might be happening.

You can benchmark it pretty easily. Just put an object on the screen and measure how long it takes to read X 1000 times. Compare that to reading it just once and storing it in a variable. Make sure to test this on a device because it actually performs about the same when in the simulator.

The reason for the slowness is that setting or changing X requires Corona to go through a Lua to Native bridge. As compared to just reading a variable in Lua, going through the bridge can be quite slow. In some cases it’s inevitable since the physics engine operates all on the native side, so there’s no way to be sure of an object’s position when it has physics applied to it without crossing the bridge.

Try measuring how long each frame takes to render. You can do this by checking system.getTimer() at the start of the frame and then subtracting that value from system.getTimer() at the end of the frame. If you want to get your app running at 60FPS, then that value needs to stay under 16 milliseconds.

I’m not sure how transitions work internally in Corona and I haven’t benchmarked it so I can’t tell you for sure if your example will be slow/faster. However, using translate() to update x and y simultaneously instead of setting them separately might be faster, assuming that it only makes one call through the native bridge (But I haven’t checked this case either). [import]uid: 135827 topic_id: 27179 reply_id: 110362[/import]

Thanks for the post.

I’m interested in your practice of “We keep everything invisible until we need to place it on the screen, and once it moves off the screen we make it invisible again.”

I thought Corona was supposed to use culling to keep from drawing off-screen objects, and therefore not slow performance. Are you really noticing that manually making off-screen objects invisible helps with performance? [import]uid: 92621 topic_id: 27179 reply_id: 110365[/import]

Thank you for your time and post! much appreciated
[import]uid: 99036 topic_id: 27179 reply_id: 110368[/import]

Hi Poon,

I think I could have been clearer on that. It’s not that we keep items invisible that matters, but rather that we don’t re-load them. Since at a given point we might have up to 8 enemies on the screen, we instantiate 8 enemy sprites when the game scene loads and then keep them in a queue to be used.

To be more specific, we have have 2 tables. One is called availableEnemies and one is called visibileEnemies. During the game we randomly take enemies out of the availableEnemies table and place them into visibleEnemies. Then on each frame we loop through the visibleEnemies table and re-position each one on the screen. Once an object in visibleEnemies has moved off of the screen, we place it back into availableEnemies. [import]uid: 135827 topic_id: 27179 reply_id: 110370[/import]

Great points!

And adding to #1: avoid adding physics bodies during the actual game cycle! In an early rendition of a “runner” game I’m working on, I would both create display objects and apply the necessary physics body before they were “scheduled” to move into view. This caused noticeable skips especially on older devices.

The newer rendition of this game creates all objects… AND their physics bodies… BEFORE the level begins. These are all set to inactive (body.isActive = false) and invisible. Think of it like a pile of puzzle pieces sitting offscreen, waiting to be used, but not yet picked up. When a particular item needs to come on screen, I just pull the proper piece from this pile, set its X and Y position, make it active, visible, and send it moving on its path. The performance gained by all of this was HUGE, and I emphasize all developers doing a fast-moving game to consider this approach.

@george18: great job on your game. Yet another example of Corona apps getting better as time passes.

Brent Sorrentino
[import]uid: 9747 topic_id: 27179 reply_id: 110410[/import]

How do you preload the image?

I take you an example i have this module…

module (..., package.seeall);  
  
function new()  
 ---CONSTRUCTOR---------------------------------------  
 local sapo = display.newImage("media/sapo.png");  
 sapo.x = 60; sapo.y = 282;  
 sapo.xScale = 0.5; sapo.yScale = 0.5;  
  
 local frogsound = audio.loadStream("media/frog.wav")  
  
 -----------------------------------------------------   
 function remove\_monster( object )  
 if object ~= nil then  
 object:removeSelf()  
 object = nil;  
 end  
 end  
  
 -----------------------------------------------------  
 -- Listener to Touch Screen   
 -----------------------------------------------------  
 function touch(e)  
 if (e.phase == "ended") then  
 local tongue = display.newLine( sapo.x, sapo.y, e.target.x,e.target.y)  
 tongue:setColor( 255, 102, 102, 50 )  
 tongue.width = 5   
 local circle = display.newCircle( e.target.x, e.target.y, 5 )  
 circle:setFillColor(255,255,255)  
 transition.to(tongue, {time = 1000; alpha = 0});  
 transition.to(circle, {time = 1000; alpha = 0});  
 audio.play(frogsound, { channel=0, fadein=100 });  
 timer.performWithDelay(2000, function() tongue:removeSelf(); circle:removeSelf() end, 1 );  
 end  
 end  
  
 function prova()  
 print("proasdva");  
 end  
 -----------------------------------------------------  
 -- Listener  
 -----------------------------------------------------  
 --Runtime:addEventListener( "enterFrame", touch );  
  
 -- TEMPLATEFOR THE FUNCTION --  
 sapo.fun\_prova = prova  
 sapo.fun\_touch = touch  
 -- -- -- -- -- -- -- -- -- ---  
 return sapo;  
end  

and i call it in a array…
for (…)
enemy[counter_enemy] = sapo.new(); – create new instance
How i can preload image in this case?!? Because for every instance that i call i create a new local variable to display and to show it…

I’m a little bit confused with the preloading of the image in Corona… [import]uid: 153233 topic_id: 27179 reply_id: 110472[/import]

Some great tips here, thanks for sharing. And congrats on a great game which looks awesome!

In regards to #4, how large are your sprites and by how much do you scale them? [import]uid: 129287 topic_id: 27179 reply_id: 110476[/import]

Don’t forget to give this a read over also: http://developer.anscamobile.com/forum/2011/12/03/tips-optimization-101 [import]uid: 84637 topic_id: 27179 reply_id: 110487[/import]

Thank you danny for this post… I really need this tricks and tips!!! :slight_smile:

[import]uid: 153233 topic_id: 27179 reply_id: 110494[/import]

So I have a question for you (I love playing GoNinja).

I’ve noticed on lower-ish end devices. There is some stutter stepping when slicing ninjas to pieces.

On ipod4g, when on iOS 4.35, I run into a samurai the screen *slightly* stutters. On ipad2, it’s not as noticeable (but it is noticeable if you are looking for it with eagle eyes lol). I’ve run into this problem as well with collection of coins and whatever on screen. Was this something you were aware of already and just had to “deal” with it, or did you know about it already and figured along the lines of scaling up the sprite and people wouldn’t really notice?

I’m just wondering if its something on your radar and what the course of action was. It’s annoying the crap out of me on my project, so I figured it might have done the same for you guys :slight_smile:
ng
[import]uid: 61600 topic_id: 27179 reply_id: 110521[/import]

This is good stuff thank you for sharing! In my game I’m doing quite a bit of positioning via .x and .y and also scaling via .xscale/.yscale… will try using translate() and scale().
Congrats on GoNinja too! [import]uid: 131038 topic_id: 27179 reply_id: 110523[/import]

What a great thread! Thank you so much for all the tips. I am also guilty on using .x and .y and .rotation too many times and because my upcoming game is a fast action game, it is limiting me to 30 fps as of now. Your tips make me want to re-look at things. I have couple questions if I may:

1- When you say (point 3): “We only check these once per frame per object and then we cache them so they don’t need to be read again…” Would you mean, doing something like this:
[lua]local tempX, tempY, tempRot
local frameCounter = 0

local function gameLoop()

frameCounter = frameCounter + 1

if ((frameCounter % 60) == 0 then
tempX = var.x
tempY = var.y
tempRot = var.rotation

end

end[/lua]

Then use the tempX,… like “if tempX > 0 then…end” rather than doing something like “if var.x > 0 then…end”

Is this what you are talking about?
2- I like the idea of “making” all the physics objects before the game play start. For instance I have bunch of asteroids that are spawned (image, positions and physics) during the game play. Not sure how many I need in any given play. I guess I could count them and see how many of them I will need (with some extra to be sure) You said you have 8 enemies at any one time. In my case, I have x asteroids traveling from outside the screen to hit some objects on the screen. Do you mean, I need to just re-purpose those asteroids that get destroyed while they are visible on the screen? My guess I will need to some kind of array that get pre-filled before a game start and then that same array will keep track of all the destroyed asteroid objects and give them a new life (new, x,y…)

3- If you go from 30 fps to 60 fps, are you not afraid to speed up all your moving display objects? Again in my case, my asteroids use physics to move around (initial impulse) and so i am afraid that they would simply move much faster on faster devices (like ipad2) and slower on slower devices (like 3GS) Or maybe this is not an issue at all because the physics engine already deal with 30 versus 60 setting?

By the way, moving objects with physics (zero gravity) is the way to go in my opinion. My game was super slow using .x and .y until the guys at M.Developers persuaded me to use the physics engine and believe me, my game went from in-playable to great at 30 fps. Now, all I need to do is optimized to run at 60!

Thanks so much again for those great tips. Keep them coming!

Mo
(LairdGames)
[import]uid: 100814 topic_id: 27179 reply_id: 110546[/import]

Brent - That’s a good point. In our case we do modify physics objects sometimes during gameplay, and I have noticed that it is one of the slower things to do. I think the newer builds have optimized some of this.

albertocubeddu - If you’re calling that constructor while the user is playing the game (instead of while the game is loading), then it could definitely hurt performance. What you can do instead is call display.newImage(“media/sapo.png”) multiple times while the game loads and put its result into an array. Then in the new() function, just retrieve an element from that array instead of calling newImage again.

Dr.Mabuse - In the HD version of our game, which we’re going to submit for iOS in the next day or two many of our sprite sheets approach 2048x2048px. The sprite sheet for the ninja is 16 frames and 2000x1600. For every image we have half hd (so, for the ninja sheet it would be 1000x800) and quarter size (500x400).

We scale them primarily using Corona’s dynamic scaling feature. However, in some cases, like for the Ipad1, we have custom code that tells Corona it to use the smallest sprite sheet available (quarter size) and then we use image:scale(2,2) to scale it up to be the same as the other half-size sprites. We have to do this to stay within the iPad1’s limited texture memory (we’ve found to top out around 75mb).

nicholasclayg - The reason for this is because when you kill an enemy or get killed we have to write data to the database in order to track achievements. Writing to the database is one of the slowest things you can do, even with very small amounts of data. One option we had was to not write to the database until the end of the game, but then there was a risk of the user accidentally closing the game and missing out on a hard-earned achievement.

Many non-corona iOS and Android games solve this by writing to the database asynchronously so it doesn’t cause any lag. Corona doesn’t support this yet unfortunately.

We might explore some other ways to reduce how noticeable this is. Maybe we can bundle database writes into a single query that runs every 5 seconds or something like that. There will still be a bit of lag when it happens, but it will be less frequent at least.

If you’re seeing this kind of lag in another game and it’s not because of database writes, then most likely it is actually something that can be fixed. There’s no reason why picking up a coin should lag a game unless the game must update some value in a database.

LairdGames -

  1. Yep.

Actually specifically what we do is this.
[lua]local function gameLoop()
image.cachedX = image.x
image.cachedY = image.y
–…
end[/lua]

That way at least the data stays associated with the object. It’s a bit neater than having more loose variables.

  1. Yep. That’s how we do it. It’s kind of annoying because you need to write a function that restores destroyed objects into fresh ones (Dead ninjas in our game need to have their frames reset into the alive position, for example), but the effort definitely pays off if you need to make your game run smoother.

  2. The physics engine should run mostly independently from the framerate of your game. Sometimes in performance limited situations the physics engine will behave differently on slower vs faster devices – The best way to avoid that is to not have frames that execute slower than the max limit for your framerate (1/30th of a second or 1/60th of a second).

The reason you’re finding the physics engine to be much faster is because it’s operating pretty much completely in highly optimized native code. The biggest drawback to using the physics engine for everything is that it can be much more unpredictable. If you absolutely must have an object travel from point A to point B with no margin for error (watch out for slow android devices!), then at least some of it has to be done in Lua.

In our case we actually use the physics engine for almost all of the ninja’s movement, however we also check every 5 frames to make sure that he’s not out of place and we manually correct him if he is. [import]uid: 135827 topic_id: 27179 reply_id: 110761[/import]

wow! George, thank you so much. A LOT of good tips here. Thanks a lot for taking the time to answer me directly. I appreciate that. I am still curious about one thing and it relates to my first question. Do you do this:

[lua]local function gameLoop()
image.cachedX = image.x
image.cachedY = image.y
–…
end[/lua]

at every frame (60) or do you “skip” frames like i showed in my pseudo code above (question 1)? I would think we would not need to cache the variables at every frame but maybe every 2-10 frames. Would using the modulo % and a frame counter the way to go?

In any event, thank you so much for the taking the time to write the above detailed answers. It is really helped putting my head around improving my app and try to get to 60!!!

Mo. [import]uid: 100814 topic_id: 27179 reply_id: 110770[/import]

LairdGames -
No problem! Really glad you’re finding it helpful.

In regards to skipping caching on some frames, it’s a good idea. Of course you don’t want to skip everything every 5 frames since then you’re not really getting any benefits of being at a higher frame rate.

[import]uid: 135827 topic_id: 27179 reply_id: 110777[/import]

Thanks George. Good point! ha! ha!

Mo

[import]uid: 100814 topic_id: 27179 reply_id: 110786[/import]