Creating a Universal Cocos2d 2.0 App
Recently I ported a game (unFed unDead) that I was working on from an iPhone app to a universal app in order to fully support the iPad platform. The game is written using Cocos2d version 2.0. I didn’t find much support in the documentation, forums or tutorial sites on how specifically to account for the different screen sizes and resolution modes. The solution that I came up with was to use C preprocessor definitions and macros.
A little more background on the problem is probably in order before I present my solution. The following table shows the supported devices as Cocos2d sees them.
Device | resource suffix | point resolution | resolution
iPhone Retina | -hd | 320×480 | 640×960
iPad | -ipad | 1024×768 | 1024×768
iPad Retina | -ipadhd | 1024×768 | 2048×1536
As you can see, the [device] vs retina [device] is a no-brainer. In fact you only need to have the appropriate images available and flip the retina enabled flag.
The issue is creating the universal app. The code needs to support both a 320×480 point display and a 1024×768 point display. There can be a LOT of translation (not just scaling) that has to happen for iPhone to iPad due to the different aspect ratios.
I didn’t see a way in Cocos2d to determine iPhone from iPad easily, so I created a global macro that I could use to determine the platform.
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
I noticed that when positioning display items the position was either based on relative position to the screen size (screen width / 2, screen height – 40) or a relative position inside a parent element.
When the coordinate for positioning is based on the screen size I use the following macros.
#define SCREENWIDTH (IS_IPAD ? 768.0 : 320.0)
#define SCREENHEIGHT (IS_IPAD ? 1024.0 : 480.0)
Almost always, if the position is based on the parent object, I simply needed to scale the coordinates. To facilitate this, I created a new macro to scale based on the device.
#define DEVSCALE(n) (IS_IPAD ? n*2 : n)
I used this a lot of times in conjunction with the ccp(x,y) macro provided by Cocos2d, so I created the following macro to use when both parameters of the ccp(x,y) macro needed to be scaled.
#define dccp(x,y) ccp(DEVSCALE(x),DEVSCALE(y))
Now I could simply prefix my ccp() calls with a d and they would be scaled appropriately.
This leaves only the elements that are not based on the screen size and are not simply a matter of scaling. These elements are ones that I wanted to reposition on the iPad since it had more real estate due to the aspect ratio. I created the following macro in order to specify that the code should use one value if running on the iPhone platform and another if running on the iPad platform.
#define DEVDEP(n,i) (IS_IPAD ? i : n)
For the sprite sheets, I decided to reuse the -hd sprite sheets, that I had created for the iPhone Retina, for the iPad standard version, since it has a similar resolution to the iPhone Retina display. Currently, this means creating a copy of the sprite sheet’s image and plist definition and changing the suffix to -ipad. Doubling our graphics footprint on the file size is not desirable, especially on a mobile platform. I will be correcting this with a modification to Cocos2d that will fall back on -hd in the case that an -ipad version of the file is not present. I’ll save that for another post.
All in all, this simple collection of macros has allowed me to quickly port the game from iPhone to Universal.
Be sure to check out the game when it becomes available for the iPad. unFed unDead is currently available in a non-retina version on the App Store.