Saturday, December 25, 2010

Cocos2D support new and old devices textures

Got a new iPhone game project going - an opportunity to learn some new things which is always fun.
I am using the cocos2d framework, which really saves me a lot of work.
I still would like to get deeper into OpenGL ES some time later, but for this project it will be enough to have cocos2d handle the low level details.

One issue I struggled with is that I need to create fairly detailed animation of the scene background.

Due to the limit on OpenGL ES texture size (especially on older devices where the texture is limited to 1024x1024 pixels), I am faced with two options:

  • Limit the number of frames (In a 1024x1024 texture I can get 9 frames of size 320x340 which is the size of my background area in the game).
  • Construct the background area from multiple animations and compose them programmatically. While possible, this makes the design work more cumbersome as the designer needs to supply the needed graphics and their location.
Using a larger texture size of 2048x2048 gives me enough room for 36 frames, which is sufficient for quite a nice background animation loop, but should I give up all older devices (prior to iPhone 3Gs)?

There are millions of those!

So this leads me to the current solution I am going to use, which is:

Use a 2048x2048 texture for newer devices, and substitute that for a smaller 1024x1024 texture on older devices in run time.

But how should I decide which texture to use?

Well, I will save you all my google searches, as the answer was found right under my nose:

Cocos2d CCConfiguration class!

Here's the code:

    NSString *plistFile;
    int numFrames;
    if ([[CCConfiguration sharedConfiguration] maxTextureSize] < 2048)
    {
        plistFile = @"highQualityAnimation.plist"];
        numFrames = 36;
    }
    else
    {
        plistFile = @"highQualityAnimation.plist"];
        numFrames = 9;
    }
   
    // cache the frames to the sharedSpriteFrameCache
   
    CCSpriteFrameCache * cache = [CCSpriteFrameCache sharedSpriteFrameCache];
    [cache addSpriteFramesWithFile:plistFile];

    // create the animation
    CCAnimation * animation = [[CCAnimation alloc] initWithName:@"animation" delay:0.1];    // set the frame rate as needed
    for ( int i=1; i <= numFrames; ++i )
    {
        NSString * fname = [NSString stringWithFormat:@"animation_frame_%i.png", i];
        [animation addFrame:[cache spriteFrameByName: fname]];
    }

    CCSprite *backgroundSprite = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:@"animation_frame_1.png"]];
    id action = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation]];
    [backgroundSprite runAction:action];

    // add the sprite to the scene layer

    [self addChild:backgroundSprite];

That's easy enough, gives good animation on newer devices, and still reasonably support for older devices.

Saturday, December 18, 2010

Easily making the different size icons for iPhone and iPad

This is a small tip for scaling your icon to the different sizes requested by iPhone and iPad applications. I used to do that with Photoshop actions in the past, but if that is not available, you can use this tip with no extra software using Mac OS X Automator:
1. I Start with a high quality 512x512 png file (which you will later rename to iTunesArtwork without the png extension) as this is needed for the App Store.
2. In Automator, create a new Service and add the following steps:
Save this service as iPhoneIcon (or icon57, or whatever).
3. Similarly, make services for the different sizes as listed here:
Icon-72.png (72x72)
Icon@2x.png (114x114)
Icon-Small.png (29x29)
Icon-Small-50.png (50x50)
Icon-Small@2x.png (58x58)
4. Now place your 512 icon in a folder, in the finder, right-click the icon file and select from the services sub-menu the different services, and voila! you got the scaled icon with the right name.
5. Add to your project resources and you are ready to go...

Saturday, December 4, 2010

File Names are Case Sensitive!

Urrrrrrgggggghhhh. Spent two days debugging an issue where my program works well on the simulator, but not on a device. I was debugging directory creations, file createions, file permissions in an attempt to find why a dwnloaded file could not be read. I am downloading a zip file from a server, and unzip it into a sub-directory of the Documents directory of my app.
I used PhoneDisk to look at the file system - all hierarchy is created and it seems the file I am trying to read is there too.
After two days of debugging and trying all kind of things, getting file not find errors, I finally realized that the file is named "File.ini" while I was trying to read "file.ini".
This works fine on the simulator, but not on the device where all file names are case sensitive.
I feel stupid, but at least fixed the bug....