Why and how to pack your textures for iOS/Android

Introduction

When I developed Don’t Feed the Trolls on X360 (using XNA), I did not optimize the textures files of the game : the game was very simple, the final hardware (X360) is very fast, so it was not necessary. I arranged the sprites so that I could get their coordinates in-game very easily. Every “troll” sprite for instance is the same size, so most of them are filled with transparent pixels. A lot of space is wasted. Have a look at the sprite sheet for the characters that I use on X360.

X360 Don't Feed the Trolls Characters Sprite Sheet

The characters texture on X360 is 2048×1024. I could easily have it into a 1024×1024 but it was a lot easier if I could have all the sprites of the same kind in a row (for example, all the bear heads are on the top row). I also made a few other sprites sheets for UI, birds, tutorial for instance.

For the first draft of my Android port, I first concentrated on the gameplay and integrate this sprite sheet as is. Of course, I quickly realized I had to spend some time to optimize as obviously, constraints on mobile and X360 are different.

Why packing your textures?

Why is proper texture packing important for games, especially on mobile? Packed textures use less pixels, and from this simple fact, the game is greatly improved in many ways:

Memory

Less pixels takes less memory. Mobile have big memory constraints and saving memory from the ressource is great. Texture ressources are often the biggest ressources.

Loading

There are less files to open, and less pixels to read, this will therefore be faster to load.

Runtime performance

When packing your textures in atlases, more sprites will share the same texture. In runtime, the GPU will then have less textures changes to make and this will therefore speed up your game. This will improve performance if you draw the sprites using the same texture in a row.

It will allow you to reduce your texture sizes. Some Android devices are performing very slowly with 2048 pixels-wide textures.

Game size

The downloadable package size will be smaller too. This is great, especially for people using 3G or with limited bandwidth that won’t download big games.

How to pack?

I first thought of packing my sprites manually but quickly searched for a tool to assist me in that tedious task. There are a few tools available to automatically pack your sprites, and I am currently using TexturePacker, and I’m very happy with it (please note that I got a free copy of TexturePacker from the developer). It’s easy to use and has a lot of functionnalities, such as:

  • Auto-refreshes the packed texture in the tool when I add images to my folders
  • Export in one click
  • Support cocos2d and therefore cocos2d-x which I am using (and many more formats).
  • Removes useless transparent pixels around sprites
  • Efficient packing

All the features are listed here.

Here is a sample of a packed sprite sheet for the Android version of Don’t Feed the Trolls made with TexturePacker:

Packed sprite sheet for Don't Feed the Trolls Android/iOSInstead of 2048×1024, this is just 1024×1024, and most importantly, I have all my characters, the UI elements, and some background elements in the same, smallest texture!

Generic code

I did some code so that resource loading does not have to deal with packs or single sprites. That way, I can send a version without packs to the artist and he can test his ressource live. In cocos2d-x, this is very easy. You first need to load your pack(s) with CCSpriteFrameCache ::addSpriteFramesWithFile( file_name ). And then, read the ressource from the file if it’s not found in the packs. I did it like this:

CCSprite *GetSprite(const char *file_name )
{
 CCSprite *psprite = new CCSprite( );
 psprite->autorelease();
 // Search in the cache, first
 CCSpriteFrameCache *sfc = CCSpriteFrameCache::sharedSpriteFrameCache();
 CCSpriteFrame *psf = sfc->spriteFrameByName( file_name );
 if( psf != NULL )
 {
  psprite->initWithSpriteFrame( psf );
  return psprite;
 }
 CCLog("WARNING : %s not in a PACK!", file_name );
 psprite->initWithFile( file_name );
 return psprite;
 }
sprite->autorelease();
// quite ugly wait to skip dlg/img directory
if( file_name[3] == ‘/’ )
{
CCSpriteFrame *psf = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( &file_name[4] );
if( psf != NULL )
{
psprite->initWithSpriteFrame( psf );
return psprite;
}
}
fgLog(“FROZAX : %s not in a PACK”, file_name );
psprite->initWithFile( file_name );
return psprite;

Conclusion

Packing your textures is very important on mobile platform, but hopefully, great tools make that task very easy and straighforward.

Do you pack your ressources? Do you do it by hand? Which tool do you use?

Follow me on twitterfacebook or google+ for more game development information. [TP:B1439B10]

Comments

  1. Pingback: Optimizing Graphics Performance on iOS and Android | Frozax Games Dev Blog

  2. Avatar jbosch

    I’m a newbie, could you post about how to use the sprite once initialized please? How do I get access to every individual sprite in the package? this

  3. This is explained in the “Generic code” section of the article. Call CCSpriteFrameCache::addSpriteFramesWithFile( file_name ) to load the texture pack exported previously. Then you retrive the sprite by its name (the name of the original file used in the pack) with the GetSprite function shown above. Once you got your CCSprite, you use it as any other sprite (add it to the scene used in the CCDirector::runWithScene).

  4. Hi,
    I have one question to ask and i want to start it from the beginning.
    Once we have packed the sprites in the texture we are placing it into the texture folder and in that texture folder we are creating sub-folders so that whenever we placed the sprites into those folders the texture packer software will automatically get us those sprites into the program one by one how we can get those sprites back one by one from the texture ?

  5. Avatar Gustavo

    Hello ,
    i want to ask , how do you do with your background images? you load in spritesheet?
    i m using texturpack and ios and android have some limitation about the size , 2048×2038 , then i have so separate in 3 or 4 big png with all images , including the background.

    Another question , did you used another resource for retina display? i dont know what to do , use retina resource and when in iphone 3gs make a 0.5 scale or have 2 resouces. What do you think about?

    thanks

  6. I didn’t put the background in the spritesheet.
    All my game was made with 960×640 in mind, and everything is scaled up or down depending on the target hardware. I do not have resource for retina iPad3.
    However, I plan to improve this greatly in my next project, as it’ll be multiplatform iOS, Android, Pc, Mac and Linux, thus supporting a wide range of resolutions and aspect ratios.

  7. Avatar Gustavo

    Ahh very nice , i made two resources , and want to know how did you made the scale? in your method that get the sprite you used sprite->setScale(0.5);

    Frozax , Today i started to test in IOS device 3GS and im having a problem with the CCSpriteFrameCache loading my plists.

    1) Same project , with same resources run in Droid2 without lag or memory problem (load all resouces) (droid2 is much worst than Iphone3GS)
    2) I cleaned all apps of 3gs Memory , so its clean
    3) First my texturePacks were configured with RGB8888 , the best , i changed to RGB4444 , so i take 1/2 of the last size of all resources.
    now its about 14Mb of resouces in 12 pngs archivies of 1024×2048
    4) I tried to load just se necessary at first , and after in the loading of the game i load some other stuffs , but looks like the memory that i can allocate is very small in 3gs.

    I received the message [12184:907] Received memory warning.

    (this is my applicationDidReceiveMemoryWarning
    – (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
    cocos2d::CCDirector::sharedDirector()->purgeCachedData();
    )

    Even with all this changes the app crashes after loading 5 or 6 plists.

    This is how i load my resources:
    CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(“images/texturePack3.plist”);

    Any Ideia for this problem? thanks

  8. My implementation is still in my head but not running for now. I am using CocosBuilder and I think I will be able to work this out with this software, using positionning relative to parent nodes. I am still not sure about scale.
    About your memory issue, maybe the 3GS does not like rectangle textures and waste memory? You can try to create square 2048×2048 texture and this how it goes (I’m just guessing here, but that’s an idea).

  9. Avatar Gustavo

    FIrst all textures were in 2048×2048 , the problem is memory , because if i comment some loads the game starts , buts when i load all the textures its crashs,and its not one texture specifically. The straiingh thing is in android runs ok , but there is some issue or configuration in IOS that could make this issue?

    i see an archieve in resources call app.icf but i think this runs with marmalade.
    Any other idea?

    very thanks

Comments are closed.