Due to technical issues (blank page) I had to split the previous article (now focuses only on memory optimization) in two. This is the second part which (mostly) focuses on reducing the app bundle size.
Loading Assets In Sequence
Here’s the code that I use to load textures or other assets asynchronously (in background, on another thread).
Imagine loadAssetsThenGotoMainMenu being a scheduled method that runs every frame or perhaps less often. The assetLoadCount and loadingAsset variables are declared in the @interface as int and BOOL respectively.
loadingAsset = NO;
NSLog(@"load assets %i", assetLoadCount);
if (loadingAsset == NO)
loadingAsset = YES;
NSLog(@"============= Loading home.png ===============");
[[CCTextureCache sharedTextureCache] addImageAsync:@"home.png"
if (loadingAsset == NO)
loadingAsset = YES;
[self performSelectorInBackground:@selector(loadSpriteFrames:) withObject:nil];
// extend with more sequentially numbered cases, as needed
// the default case runs last, loads the next scene
MainMenuScene* mainMenuScene = [MainMenuScene node];
[[CCDirector sharedDirector] replaceScene:mainMenuScene];
When this method runs the first case statement is executed. To avoid accidentally loading the same image multiple times every time the selector runs, the loadingAsset flag is set to YES. When the texture cache has completed loading this texture, it will call the increaseAssetLoadCount. This then ensures that the next case statement is executed the next time loadAssetsThenGotoMainMenu runs.
The cool thing about this solution is that you can easily add more switch statements to add more textures to load. Because the default case is where the scene changes, and that only happens if there are no more switch cases to process.
Be sure not to skip a number in the switch cases because that will also run the default case.
Decreasing the size of your app
Besides the memory usage advantage, reducing the color bit depth of textures to 16 bit will also significantly reduce their size. But there are other options that will allow you to reduce your app’s size, perhaps significantly.
TexturePacker PNG Optimization
If for some reason you still want to use PNG files instead of the highly recommended .pvr.ccz file format, TexturePacker has a slider named “Png Opt Level” to help reduce the size of PNGs (doesn’t affect loading time though):
As far as I understand it, it tries a given number of optimizations and picks the one that creates the smallest file size. The downside is that the maximum level can take very long for large texture atlases, in some cases 10 to 20 minutes on a Late 2009 27″ iMac. The task is multi-threaded, so it should be a lot faster on quad core systems.
Fortunately there’s really no need to do this unless you’re ready to release the app. Question is, how much can it reduce the size of PNG files?
My largest PNG was reduced from 2.4 MB to 2.2 MB. A smaller one was reduced more significantly (percentage wise) from 180 KB to 130 KB. It may not sound like much but it’s nice to be able to cut down PNG file sizes from perhaps 18 MB to 16 MB total.
Note, however, there’s one important change to your Xcode project that you must not overlook, otherwise your PNG files will be inflated again when they’re packed into the bundle: turn off PNG compression in your target’s Build Settings:
With PNG compression enabled, Xcode will run it’s own PNG optimizer and the PNG files end up being just as large as they were before optimization. So make sure this setting is disabled.
Be sure to check your app’s size to make sure that not optimizing PNG files at all still reduces your app bundle size. Because you’re probably using PNG files that aren’t created by TexturePacker.
Check your app’s App Store size
Here’s how you can easily check your app’s size. In Xcode, run an Archive build (Product -> Archive). When the build is successful, the Xcode Organizer window is brought up where you can click the “Estimate Size” button to get an estimate of your app’s App Store size:
Remove unused resource files
Over time you’ll add, remove and replace assets in your game. Frequently you still keep using some of the files, others you haven’t removed from the project yet for whatever reason. It’s often fruitful and eye-opening to go over all of your project’s resource files and verify that they’re currently in use. If not, remove them from the project or at least from the app’s target.
Especially if you work on multiple targets (perhaps an iPad or OS X version) you might have accidentally added resource files to a target that doesn’t use that file.
Be sure to thoroughly test your game after removing resource files. There’s always the chance that you “missed a spot”. If in doubt (or not pressed for bundle size) it’s usually better to just leave the files in.
Reduce Audio file sizes
Sometimes overlooked, but not taking care of your audio file formats can be wasteful. Both in terms of memory usage and bundle size. Here are the best ways to ensure audio file sizes are kept to a minimum. For all these changes I recommend the free audio editing tool Audacity.
Stereo to Mono – Your MP3 files may be using 2 channels (stereo), but are they using them effectively? If you don’t notice the stereo channel to make any difference, reduce the MP3 files to a single channel (mono). This cuts file size and memory usage in half.
MP3 Bit Rate – Any sample rate above 192 kbps is essentially a waste on iOS devices. You have to apply some “trial and earror” though to find the lowest sample rate which still sounds good. 96 to 128 kbps is usually the sweet spot for MP3 files. If in doubt, make sure you listen to the audio file on the device, with and without Apple speakers – playing the audio file on your computer’s speakers may sound far better or far worse!
Sample Rate – Most audio files use a sample rate of 11, 22, 44 or 48 kHz. The lower the sample rate, the smaller the file size. But this also lowers audio quality. For example 11 kHz sounds like a telephone, 22 kHz sounds like good radio reception and 44 kHz is said to be “CD quality” with 48 kHz being even better (only audiophiles with high end equipment can hear the difference).
In many cases 44 kHz or higher is overkill, so try to reduce the sample rate by resampling the audio file (Audacity: Track -> Resample). Do not just set the sample rate, as this will change the pitch of the audio file.
Streaming MP3 Files
Depending on how you play audio (AVAudioPlayer, CocosDenshion, ObjectAL, straight OpenAL) you may be accidentally or willfully playing MP3 files using playback from an audio buffer. That means the MP3 file is loaded into memory, converted into an audio buffer (ie uncompressed) and then played back.
From what I know, CocosDenshion’s SimpleAudioEngine has a playBackgroundMusic option that streams MP3 files. Streaming has two advantages: a much smaller memory footprint, and the decoding of the MP3 file is done by the iOS hardware instead of the CPU – unless you stream more than one MP3 at the same time, in which case only one is decoded in hardware.
Reduce Tilemap Size
Many developers don’t realize that large tilemaps consume a lot of memory. Assume a 1,000×1,000 tilemap, that would require about a Megabyte of memory – if each tile only used a single byte. However my best estimate for how much a tile consumes comes closer to 64 Bytes. That means this tilemap uses around 60 MB of memory. Ouch!
Besides writing an optimized tilemap renderer, the only option may be to cut down the size of the tilemap. Perhaps splitting it in two if possible.
That was all?
Uhm, yes. I cut it short a bit this time. I hope you understand.
|Follow @gaminghorror||Follow @kobold2d|