Who should read this
This guide is for those of you who want to create a RetinaDisplay game. The game might also support the low-res too.
This guide is valid for cocos2d v0.99.5-rc0 or newer. This document is not valid for previous cocos2d versions.
RetinaDisplay in cocos2d
Introduction
The entire cocos2d API was converted to Points. Previous versions were using Pixels.
cocos2d v0.99.4 has RetinaDisplay support, however it required you to use two different sets of positions depending on the device , since the API was in Pixels.
For example to position an sprite on the top-right corner, you had to do:
// in v0.99.4
if
(
iPhone4isUsed)
sprite.position =
ccp(
960
,640
)
;
else
sprite.position =
ccp(
480
,320
)
;
But in v0.99.5-rc0 (and newer) the only thing that you have to do is:
// v0.99.5
sprite.position =
ccp(
480
,320
)
;
Point vs. Pixel
What is a pixel ? A pixel according to wikipedia is this: http://en.wikipedia.org/wiki/Pixel .
The difference between Pixels and Points is that the logical coordinate space is measured in Points , while the device coordinate space is measured in Pixels .
Example:
Device | Points | Pixels |
---|---|---|
iPhone 3GS or older [1] | 480×320 | 480×320 |
iPhone4 in LowRes mode [2] | 480×320 | 480×320 |
iPhone4 in HighRes mode [2] | 480×320 | 960×640 |
iPad | 1024×768 | 1024×768 |
Mac | W x H | W x H |
[1]: It is also valid for iPod Touch 3rd generation and older devices
[2]: It is also valid for iPod Touch 4th generation
So, only on iPhone4 when HighRes mode is enabled 1 Point == 4 pixels (2×2 pixels), otherwise 1 Point == 1 Pixel.
The API in Points
As mentioned previously, in v0.99.5 the API is in points, it means that:
// director
CCDirector *
director [
CCDirector sharedDirector]
;
CGSize sizeA =
[
director winSize]
; // is in Points
CGSize sizeB =
[
director winSizeInPixels]
; // in Pixels
CGSize sizeA =
[
director displaySize]
; // is in Points
CGSize sizeB =
[
director displaySizeInPixels]
; // in Pixels
// Node
CCNode *
node =
[
...]
;
CGPoint posA =
[
node position]
; // is in Points
CGPoint posB =
[
node positionInPixels]
; // is in Pixels
CGSize sizeA =
[
node contentSize]
; // is in points
CGSize sizeB =
[
node contentSizeInPixels]
; // is in pixels
CGRect rectA =
[
node boundingBox]
; // is in Points
CGSize rectB =
[
node boundingBoxInPixels]
; // is in pixels
// Sprite
CCSprite *
sprite =
[
...]
;
CGRect rectA =
[
sprite rect]
;
CGRect rectB =
[
sprite rectInPixels]
;
This is also valid for the setters . eg:
[
sprite setPosition:
ccp(
x,y)
]
; // in Points
[
sprite setPositionInPixels:
(
x,y)
]
// in Pixels
etc… The methods are ONLY in pixels if the methods or properties end with the “InPixels” suffix. If they don't have that suffix it means that they use Points.
When should you use Points, and when Pixels
95% of the time you will want to use the Point API , but there are special cases when you might need to use the Pixel API .
Some of cocos2d's complex objects use the Pixel API . eg: `CCSprite`, `CCTMXLayer`, `CCLabelBMFont`.
So, you might need to use the Pixel API if you are developing a complex object, specially if you need to parse a data file that has values in pixels.
The -hd suffix
Most likely, you'll want to have low-res images on iPhone 3GS and older devices, and high-res images on iPhone 4.
Apple uses the ”@2x” suffix, but cocos2d doesn't use that extension because of some incompatibilities. So cocos2d has its own suffix: the ”-hd” suffix.
If you want to use your own suffix, you can do it by editing the ccConfig.h file.
// Modify it to use your own suffix
#define CC_RETINA_DISPLAY_FILENAME_SUFFIX @"-hd"
WARNING : It is NOT recommend to use the ”@2x” suffix. Apple treats those images in a special way which might cause some bugs in your cocos2d application.
The only exception is the “Default@2x.png” image. Since cocos2d is not loaded at that time, you can use that image in order to have a High Res splash image.
Each file opened by cocos2d (image file, config file, sprite sheet, etc…) is opened using the CCFileUtils
class. When RetinaDisplay is enabled, this class will try to open the ”-hd” file instead. If that file doesn't exist, it will open the originally requested file.
Example: If you try to open the file “sprite.png” when RetinaDisplay mode is enabled, then the following will happen:
-
Obtain the full path of “sprite.png” → ”/Volumes/XXX/sprite.png”
-
Is the Application in Retina Display mode ?. If not, then return the fullpath name. If yes, then continue with step 3.
-
Does “sprite.png” have the ”-hd” suffix ?
-
In this case particular case, the answer is No, so it will try append the ”-hd” suffix to “sprite.png” → “sprite-hd.png”
-
Does the HD image exist (”/Volumes/XXX/sprite-hd.png”) ?
-
If it does exist it will use it, otherwise it will use the non-hd image
Same example, but trying to open the file “sprites-hd.plist”
-
Obtain the full path of “sprites-hd.plist” → ”/Volumes/XXX/sprites-hd.plist”
-
Is the Application in Retina Display mode ?. If not, then return the fullpath name. If yes, then continue with step 3.
-
Does “sprites-hd.plist” have the ”-hd” suffix ?
-
Yes. So, try to use it.
As you can see, the ”-hd” suffix is valid for any file: Images (.png, .gif, .bmp, etc), TMX files (.tmx, .png), BMFont files (.fnt, .png), Sprite sheets (.plist, .png).
cocos2d, as of v0.99.5-rc0, doesn't support any other suffix, like an ”-ipad” suffix, but it will support it in the near future.
Enabling Retina Display mode
You should enable RetinaDisplay mode ONLY if you want to use HighRes images on an iPhone4. Remember that an iPhone4 also works in “low res” mode.
// Add this code in your Application Delegate, right after initializing the director
// Director Initialization
[
director setOpenGLView:
glView]
;
// Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if
(
!
[
director enableRetinaDisplay:
YES
]
)
CCLOG(
@
"Retina Display Not supported"
)
;
CCNode details
CCNode
-
position
is in points -
positionInPixels
is in pixels -
contentSize
is in points -
cotentSizeInPixels
is in pixels -
boundingBox
is in points -
boundingBoxInPixels
is in pixels
CCLabelAtlas
The API is in Points:
-
+(id) labelWithString:(NSString*) string charMapFile: (NSString*) charmapfile itemWidth:(int)w itemHeight:(int)h startCharMap:(char)c;
It means that the width and height are in points. So, if you call this method when RetinaDisplay mode is enabled, you MUST have a -hd image.
Quick way to create a “pseudo” high res image by using ImageMagick:
$ convert fps_label.png -scale 200% fps_label-hd.png
WARNING : You should NOT scale up a raster image. You won't gain quality. The above given example is a quick way to create an ”-hd” image so that you can test your game quickly.
CCLabelBMFont
The API is in points, so if you want to have both a low res and high res version, you have to create a 2x file by using Hiero (or your favorite BMFont editor).
Example: If have a low res BMFont called times32.fnt (and times32.png ) (Times New Roman font with a font size of 32), then you'll have to:
-
Create a Times New Roman file with a font size of 64.
-
You should save this file: times32-hd.fnt (and times32-hd.png )
This will create REAL HighRes images.
TMX maps
The API is in Points, so if you want to create a HighRes version you'll have to:
-
copy map.tmx to map-hd.tmx
-
Edit “map-hd.tmx” and multiply by 2 the tile size and spacing in the tileset information like
tilewidth
,tileheight
,spacing
andmargin
. -
Then you'll have to create a HD version of the tileset image
Sample of map.tmx :
<map version="1.0" orientation="orthogonal" width="10" height="10" tilewidth="32" tileheight="32">
<tileset firstgid="1" name="Desert" tilewidth="32" tileheight="32" spacing="1" margin="1">
<image source="tileset.png"/>
</tileset>
Sample of map-hd.tmx :
<map version="1.0" orientation="orthogonal" width="10" height="10" tilewidth="64" tileheight="64">
<tileset firstgid="1" name="Desert" tilewidth="64" tileheight="64" spacing="2" margin="2">
<image source="tileset-hd.png"/>
</tileset>
Quick way to create a “pseudo” high res image by using ImageMagick:
$ convert tileset.png -scale 200% tileset-hd.png
WARNING : You should NOT scale up a raster image. You won't gain quality. The above given example is a quick way to create an ”-hd” image so that you can test your game quickly.
Spritesheets (Zwoptex / TexturePacker / .plist)
You should generate the files (.plist and .png) again, this time using the high res sprites. Example: If you have generated your spritesheet.plist and spritesheet.png files using the these 4 low-res images:
-
sprite1.png
-
sprite2.png
-
sprite3.png
-
sprite4.png
Then you'll have to create an HD version of those 4 images. But you should NOT rename them. Instead, what you should do is to place the HD files in another directory. eg:
$ mkdir hd_sprites
$ convert sprite1.png -scale 200% hd_sprites/sprite1.png
$ convert sprite2.png -scale 200% hd_sprites/sprite2.png
$ convert sprite3.png -scale 200% hd_sprites/sprite3.png
$ convert sprite4.png -scale 200% hd_sprites/sprite4.png
Then you will have to generate the spritesheet-hd.plist and spritesheet-hd.png files from the sprites that are located in the “hd_sprites” subdirectory.
WARNING : You should NOT scale up a raster image. You won't gain quality. The above given example is a quick way to create an ”-hd” image so that you can test your game quickly.
Sprites
The sprite API is in points:
-
-(id) initWithFile:(NSString*)filename rect:(CGRect)rect;
-
The property
rect
is in points -
But the property
rectInPixels
is in pixels
So, if you are going to use this method, be sure to have an ”-hd” image, otherwise your sprite won't look good.
Quick way to create a “pseudo” high res image by using ImageMagick:
$ convert spritesheet.png -scale 200% spritesheet-hd.png
WARNING : You should NOT scale up a raster image. You won't gain quality. The above given example is a quick way to create an ”-hd” image so that you can test your game quickly.
See also: