Hi Corona devs! I was the lead developer of ShaqDown (Currently featured on the Corona Labs homepage). You can check it out at http://shaqdown.com
ShaqDown is our second release and the third Corona game we’ve worked on, so we’ve had a lot of opportunity to learn how to build great Corona games.
Overall Corona has been an incredible platform for us. I never imagined we could build games this good with a small team and a short timeframe. The cross platform support has been particularly excellent. Our code is essentially identical on both platforms and performance is superb.
Here’s a list of some of the things we’ve learned that will hopefully be useful for you guys:
Essential tools
- SpriteHelper - By far the most valuable tool we’ve used has been SpriteHelper. Much of ShaqDown just would not have been possible to build in any reasonable amount of time without it.
SpriteHelper lets you put all your sprites into sprite sheets, crop any whitespace, as well as instantaneously generate and modify animations. Then it saves out up to 3 versions for different resolution devices. We probably have over 500 sprite frames, and around 100 animations, so there’s no way we could have managed without it.
-
GlitchGames open source libraries - Unfortunately we found out about these towards the end of our development, so we only ended up using GGRating. But next time around we might end up using every single one. The code quality is excellent and they have a library for just about everything you might want to do.
-
loq_profiler - This is a little hidden gem that I was able to dig up. It’s a beautiful plugin that displays a graph of frame rate and memory usage over time in a transparent box at the top of your game. It’s part of the sprite loq api (http://www.loqheart.com/spriteloq/apidocs/files/index-lua.html), but you don’t need to have spriteloq or use any other parts of it. Just download that file and copy out loq_profiler and loq_util, require them in your app, and you’re done. Very useful for tracking down memory leaks and frame rate performance issues.
-
tnt transitions library. A library that lets you pause, resume, and change the speed of transitions and timers. Seriously amazing. It’s available here: http://developer.coronalabs.com/code/pausable-timers-and-transitions-speed-adjustment
Make sure to follow the directions and call cleanTimersAndTransitions on a timer, or it will leak tons of memory.
- PushWoosh.com. Hosted push notification service. We had to implement some special code to get this working right (Around generating unique device ids and saving them). But its been working great. Corona cloud has push notification support too, so that’s probably even better
Little known but important tips
-
Texture memory is not the only story. Audio is loaded into memory uncompressed too!. A 2 minute track will be around 20 megabytes. Roughly 170kilobytes memory usage per second of audio loaded. I don’t know for sure how streaming audio works, but I suspect that at some point the entire streamed audio file is loaded into memory.
-
Memory limits seem to be as follows for each device type. Remember this includes uncompressed audio and textures, and is a very rough estimate:
iPad1: 90MB (But resolution same as iPad2!!)
iPad2: 160MB
iPad3: 280MB
3GS: 70MB
iPod Touch 3rd gen: 90MB (But resolution same as iPhone4!!)
iPhone4: 160MB
iPhone4S: 160MB
iPhone5: ?
iPod Touch 4th Gen: ?
Old Android Device (Droid Incredible): 70MB.
Newer Android Device: (Samsung Galaxy S3): 160MB+
-
If you use textures that have a width or height larger than 1024px, you must use the largeHeap setting or your app will randomly crash on some android devices (The Kindle Fire HD seems to be a common one).
-
The only cross-platform compatible audio formats are Wav and mp3. Wav is uncompressed so it will make your app download huge, and mp3 has licensing restrictions that could put you at legal risk unless you pay for a license to use it.
Fortunately, there’s an easy fix. Compress audio to ogg for android and to m4a for iOS, then load the right file depending on which device you’re on. There’s an amazing free tool that will quickly convert to these for you called Max: http://sbooth.org/Max/
-
Transitions appear to be faster performance-wise than repeatedly modifying X and Y attributes on each frame. We use them like crazy! The tnt library (mentioned above) has been a huge help.
-
Pop up dialogs with 3 buttons will fail to show a caption on landscape apps in iOS. The Glitch Games GGRating library is affected by this. Be careful!
-
The Android emulator is a great tool to test apps. I posted a forum thread a few weeks ago with directions on how to get it to work with Corona.
-
If you use GameCenter on a landscape app, make sure to use the CoronaUseIOS6LandscapeOnlyWorkaround workaround, or your app will crash on iOS6.
-
Texture memory used by images is determined by rounding up the width and height to the next power of two, then taking the larger of the two values, squaring it, then multiplying it by 4, and then you have number of bytes used. So an image that is 1000x600 will get rounded up to 1024, square that and you get 1048576, multiply by 4 to get 4194304 bytes (Exactly 4 megabytes).
Or to simplify matters,
width or height rounds up to 512px: 1mb memory used
width or height rounds up to 1024px: 4mb memory used
width or height rounds up to 2048px: 16mb memory used
width or height rounds up to 4096px: 48mb memory used
You can see why using sprite sheets is helpful. If you have an image that is 600x600, it gets rounded up to 1024x1024. If you made it 1024 in the first place and used the extra space to store other sprites, then it’s basically a chance to put in more images with zero more memory usage. Use SpriteHelper to help you do this and you’ll save tons of texture memory!
-
If you want to push the boundaries with Corona, be extremely careful about managing memory leaks. It’s extremely easy to leak memory in Lua. Also, there’s either a Corona bug or SpriteHelper bug that causes entire spritesheets to not be garbage collected if animated sprites are loaded in a require()'d file and that file is not unrequire()'d.
-
Some Android devices fail to download apps on the Google Play market if that app is greater than 30MB in size. The Samsung Captivate seems to be one affected device. A lot of apps seem to ignore this fact, but if you can get under 30MB, then you should. (ShaqDown is 29MB on Android – barely made it!)
-
Unicode strings are not supported by Lua string functions. But there is a good library with functions that supports them https://github.com/alexander-yakushev/awesompd/blob/master/utf8.lua (Important if you’re trying to localize)
-
Dispose of unused audio tracks and sounds, but don’t dispose of them twice or your app will completely crash. I wish Corona protected against this automatically, but until it does, be careful how you handle audio.
-
Straying from the default content size of 320x480 can cause a lot of headaches. There was a good Corona blog post about how to have device-specific resolution, and that method is probably ideal. But unless you know what you’re doing, I wouldn’t change this value. For our first app we used 640x960 and it caused a lot of headaches when trying to use tools like SpriteHelper.
-
Use 60FPS instead of 30. Especially if you’re building an action game. If you’re careful then you should be able to get good performance at 60FPS for most games and it makes the game look much smoother.
-
The FPS that you set your game to affects the physics engine. So changing it later can cause headaches.
-
Objects nested in groups, where the parent group is moved no longer correctly collide with each other and the hybrid overlay becomes inaccurate.
This is a huge problem if you want to have grouped sprites with separate collision areas. For example – A ship that flies around where each wing is a different sprite with its own collision behavior.
The best solution for this problem appears to be to generate an invisible sprite that detects collisions placed in the same group as your other physics objects and then align it visually on top of the nested sprite by using localToContent() within a timer.
If you get to the last level of ShaqDown, you’ll see this taken to the extreme. I build a join system for the boss, and then each body part of the boss has an invisible copy that handles collision that is placed on top of the visible body part and then correctly rotated and positioned on a frequently run timer. Hopefully you won’t need to do anything quite so crazy 
- Dither your PNGs if your app build is getting too big. Dithering PNGs results in relatively minor quality loss, but huge space savings (around 70%).
Corona bugs to watch out for
In general Corona is amazingly stable and the quality of the Daily Builds in particular is top notch. Still, there are bugs in all software, and some of the following can really sting.
I know I should report all these using the bug tracker, but I just haven’t had the time to put together good test cases. Some of these issues may also be already known by the Corona, but perhaps not by some developers.
- Storyboard is really, really buggy. Or at least it was buggy and the latest patch may have fixed it (But I’m pretty sure it’s still buggy).
Here’s some of the weird things I’ve seen.
Paramaters dont get passed in some cases.
reloadScene() sometimes just doesn’t work.
exitScene is sometimes not called.
enterScene is sometimes not called the second time a scene is loaded if the scene is unloaded a certain way
Scenes are sometimes not removed from memory completely.
Admittedly, some or all of these might be from incorrect implementation on my side, but the documentation is quite poor too.
I ended up putting in some insane workarounds that may have just made the problem worse, but they seem to be holding up for now. Things like calling exitScene before calling loadScene, or setting self.view to nil at the end of my exitScene handler.
At some point I want to go through and figure out exactly what’s broken in each case (or what I’m doing wrong), but I just haven’t had the time. Maybe another ambitious Corona dev can submit some good bug reports (Or put together a good tutorial on advanced usage of Storyboard)
-
As I mentioned above, if you load spritesheets bigger than 1024x1024 you must use largeHeap on android or you will get random crashes (especially on the Kindle Fire). I also noticed this issue in the Android emulator and on the Sony Tablet S.
-
Sound sometimes lags on Android. A fix that was mentioned was to use media.playEventSound on Android, but that may also have its own problems. In either case, test carefully to make sure audio works as expected and the volume control works the way you want it to. There doesn’t appear to be an ideal solution for audio issues on Android yet, but it’s worth spending a few minutes to figure out the best solution for your app.
In our cases we ended up using the normal audio API. Reviews on Android have been great, so I guess it worked out OK for us.
- GameCenter can be really screwy. The app is not notified if the user cancels the game center log in popup, but then if the app tries to launch the game center popup again then nothing happens (And the app is not notified). Suspending the app and resuming it seems to cause the GameCenter pop up to be visible again? Weird!
Fortunately this is more of a problem during testing than anything else, but it can be quite frustrating.
- Siri and Phone Calls can cause audio to stop playing on iOS. This seems to affect a lot of iOS apps, so it’s not necessarily a Corona issue. But this topic comes up from time to time on the forum, so I think it’s worth noting.
I hope at least some of this is helpful. Thanks for reading, and I hope you all enjoy ShaqDown!
I’ll be watching the comments and I’m happy to answer any questions if you have them 
[import]uid: 135827 topic_id: 34739 reply_id: 334739[/import]
