High-res graphics in Cocos2D

This is the second of two posts about supporting high-res graphics in iOS games. The first post was a general overview called Retinafy Your Game, and this post is a more specific look at how I implemented high-res graphics in Cocos2D. I’d recommend reading the first post before you read this post, so that you have an idea of some things to consider when adding Retina support to your game.

What did you do?

Nothing too crazy. The point of this technique was to change Cocos2D to support Retina graphics without having to change any of the actual game logic or code. Once you’ve made the changes to Cocos2D, all you have to do is supply high-res images. This also gives you nice looking high-res graphics on the iPad in 2x zoom mode without having to make your game universal.

Warning: Cocos2D is in a constant state of change. I used the newest develop branch to do these changes, but as long as you’re using 0.99.4 or above you should be able to get it to work. It’s possible to make it to work with even earlier versions, but it’ll take a bit more effort. There’s a good chance you’ll run into some issues while putting this into your own game, so feel free post a comment, email me, or message me on Twitter. There’s also a thread discussing this on the Cocos2D forum.

Ok, but what did you actually do?

I won’t go through every change line-by-line, instead, you can see the diffs for my changes on GitHub. I removed the Mac things from my branch because they have nothing to do with Retina support. The gist of the changes is this: we scale the root of the display tree by 2x when we’re in high-res mode, so that it’s double the size. Whenever we grab a texture in CCSprite when we’re in high-res mode, we double the size of the rect we’re grabbing. That’s about it, aside from a couple extra fixes to make point particles and CCRenderTexture work properly, as well as some changes in the code that converts between OpenGL and UIKit points.

How do I use it?

Either grab the code from GitHub, or go through the GitHub changes line-by-line and add them to your own Cocos2D files(there really aren’t that many changes, so this is probably the easiest option.). Once you’ve done that, you have to start your CCDirector a fairly specific way in your AppDelegate. This is how I did it:

//
//
_window = [[UIWindow alloc] initWithFrame:CGRectMake(0,0,320,480)];
[_window setUserInteractionEnabled:YES];
[_window setMultipleTouchEnabled:YES];

BOOL isHD = NO;

if([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) //iPad and iPhone 4
{
	if([[UIScreen mainScreen] scale] == 2)
	{
		isHD = YES;
	}
}

if([[[UIDevice currentDevice] model] rangeOfString:@"iPad"].location != NSNotFound)
{
	isHD = YES;
} 

if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
	[CCDirector setDirectorType:kCCDirectorTypeNSTimer];		

CCDirector *director = [CCDirector sharedDirector];
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
[director setDisplayFPS:NO];
[director setAnimationInterval:1.0/60];		

EAGLView *glView = [EAGLView viewWithFrame:CGRectMake(0.0, 0.0, 320, 480)
							   pixelFormat:kEAGLColorFormatRGBA8
							   depthFormat:0 /* GL_DEPTH_COMPONENT24_OES */
						preserveBackbuffer:NO];	

if(isHD)
{
	[director setContentScaleFactor:2.0];
}

[director setOpenGLView:glView];	

[director setProjection:kCCDirectorProjection2D];

[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];	

[_window addSubview:glView];
[_window makeKeyAndVisible];

[[CCDirector sharedDirector] runWithScene:[DemoSceneA node]];
//
//

My changes only work for 2D projection mode, but I’m sure someone who knows OpenGL better than me could get it to work in 3D projection mode as well.

The one last piece of code is a simple macro that’ll load images with an HD suffix when you’re high-res mode (eg. demoAtlas.png becomes demoAtlasHD.png).

//
//
#define GetPNGFileName(__imageName__) (([[CCDirector sharedDirector] contentScaleFactor] == 2) ? [NSString stringWithFormat:@"%@HD.png",__imageName__] : [NSString stringWithFormat:@"%@.png",__imageName__])

//Usage
CCSprite *someSprite = [CCSprite spriteWithFile:GetPNGFileName(@"someSpriteName")];
//
//

That’s about it. I created a full demo project which demostrates the technique in action. I tested it on an iPod Touch, iPhone 4, and iPad, and it works great on all of them. I also tested it in all four orientations. Please let me know if you run into any issues with it. Get it here: http://struct.ca/files/hires/HighResDemoProject.zip

This blog is part of a group of awesome blogs that make up iDevBlogADay. If you’re an iOS developer you should definitely check it out. You’ll get at least two fresh blog posts per day. Delicious.

This entry was posted in Cocos2D, iDevBlogADay, iOS Development. Bookmark the permalink.

29 Responses to High-res graphics in Cocos2D

  1. Pingback: Retinafy Your Game | Struct.ca

  2. BenE says:

    Can you explain what setContentScaleFactor:2.0 does? If I guess correctly, it shrinks everything by 2x and then you have to provide 2x larger images with more pixels to compensate and utilize the higher resolution. What happens to positioning and touch coordinates?

  3. Matt says:

    BenE: setContentScaleFactor was already in the newer Cocos2D builds, but I expanded on it to make it work the way I wanted it to.

    You’re correct, it basically shrinks everything by 2x, and then you provide larger images, so that those shrunken images have more detail. All OpenGL coordinates get halved, because they’re in 640×960, but the UIKit coordinates are already scaled correctly. As a “user”, you don’t really have to do anything, you build your game and respond to everything as if the resolution is always 320×480.

  4. Thanks Matt.

    Just about to get started on a project in Cocos 2D so this is a well-timed post for me.

    Any advice for how to integrate iPad “HD” scaling into the same project? Just use a different scale factor? As a scale factor, 2.133333333 isn’t so nice though!

    Ken

  5. mrTortex says:

    “As a “user”, you don’t really have to do anything, you build your game and respond to everything as if the resolution is always 320×480.” – Is this true of Chipmunk calls also?

  6. Matt says:

    Ken: if you want a proper, full-screen universal iPad app, I’d recommend making those graphics separately, and treating it like a different project. I think that if an app is universal it should have a different layout that’s been designed specifically for the iPad. If you’re ok with “iPhone emulator mode” in the iPad app (ie. 1x + 2x zoom), then you don’t have to do anything to get it to work, and the graphics will look great and not blurry at all.

    mrTortex: yep, you won’t have to change any of your Chipmunk or Box2D code.

  7. TheLittleGuy says:

    Thanks for sharing Matt :)

  8. Thomas says:

    What does this mean for texture sizes though? The limit for the size of 1 texture is 1024 x 1024. Can I simply use a 2048 x 2048 and it will work right? Or will the limitations of cocos2d block this? Is there simply a way to take the original graphics and boost them to 2x without antialiasing them? In my case I have a game made with pixel art so really all I need to do is scale the entire contents of my game up to that of iPhone4, but if its blurry I can’t afford that with such graphics. What would be the best way to do it?

    • Matt says:

      The devices that have high-res graphics (iPad and iPhone 4) both support 2048×2048 textures, so that’s definitely fine.

      For your case, you may want to look into a different way of doing it. It’s possible to change the scaling algorithm that’s used when your game is upscaled, which should work perfectly for pixel graphics. Check this thread out for more info: https://devforums.apple.com/message/250025#250025

  9. Josh Kahane says:

    Hi Matt, this really helped thanks! I used your code above to fix the scaling, worked well.

    However after implementing a simple menu using this code:

    CCMenuItem *playBtn = [CCMenuItemImage itemFromNormalImage:@"Play.png" selectedImage:@"Play2.png" target:self selector:@selector(playGo:)];
    CCMenuItem *gcBtn = [CCMenuItemImage itemFromNormalImage:@"gameCenter.png" selectedImage:@"gameCenterTwo.png" target:self selector:@selector(gcGo:)];
    CCMenuItem *helpBtn = [CCMenuItemImage itemFromNormalImage:@"Help.png" selectedImage:@"Help2.png" target:self selector:@selector(helpGo:)];
    CCMenu *menu = [CCMenu menuWithItems:playBtn, gcBtn, helpBtn, nil];
    [menu alignItemsVerticallyWithPadding:10.0f];
    menu.position = ccp(160, 280);
    [self addChild:menu];

    Work fine on standard simulator, however in retina display form, on iphone 4 they don’t function at all. Odd. They down get their down form when pressed and don’t change layer/scene.

    Anything which might effect this? Thanks.

    • Josh Kahane says:

      Ok, Just found something out. While the buttons look like they are positions correctly, the spot you press in the screen is offset on the retina screen.

      How can I fix this?

      • Matt says:

        Hmm. That should work. Are you having that problem with all touch events, or just with menu buttons? (or are they your only thing that responds to touches?).

        Either way, you might want to check this thread: http://www.cocos2d-iphone.org/forum/topic/8511/page/5

        Riq has now started to implement this in the develop branch of Cocos2D, so it’s already a lot more robust and bug free than my stuff is, go check it out, see if it works for you :)

        • Josh Kahane says:

          Well, I haven’t tried any touch events yet, but I don’t need them, only menu items at the moment.

          I have tried with the latest build in the developer branch and still won’t work. Back to square one, like before even adding your code, scaling doesn’t even work.

          But thanks for pointing me in the right direction! Hopefully there will be a fully fledged fix soon.

  10. Hao Zhe XU says:

    Thanks for your article, I have a question, if we make app in this way, when we upload it, for the iPad screenshots, do we just upload the 2x high-res screenshots(from iPhone 4 or latest iPod Touch but not actually from iPad)?

    Thanks!

  11. Hao Zhe XU says:

    Also, another question, if iPad app starts in 1x (ie no zoom) mode, then the user switches to 2x zoom, I doubt the app can reload all -hd graphics.

    • Matt says:

      The way I do it, I always load the high res graphics, even when the app starts in 1x, because downscaling the graphics isn’t a problem, it’s upscaling that looks bad.

      • Hao Zhe XU says:

        Yes I was also thinking of this, but if an app uses lots of graphics, always loading the hi-res ones may affect performance on some older devices e.g. iPod Touch 2nd and iPhone 3G.

        • Matt says:

          You should always load the low res graphics on the old devices.

          On the iPad, iPhone 4, and new iPod Touch, you should always load the high res graphics.

  12. Alex says:

    Hi Matt !
    I am currently developing a game using cocos2d 0.99.3. I would like to support retina display as well. Unfortunately, the game is close to finish, and it will take even more time to convert the framework to 0.99.4 or higher. What else do i need to do based on your post here, to easily support retina display ? I just read this in your post : “It’s possible to make it to work with even earlier versions, but it’ll take a bit more effort”. Thanks… =)

    • Matt says:

      Hmm, unfortunately although it would be possible, it’s been so long since I did this that I don’t remember the exact steps anymore. I’d actually recommend upgrading to the newest version of Cocos2D, which has most of this stuff built in. It really shouldn’t be that much work to convert your current stuff to it.

      • Alex says:

        Thx Matt ! Guess we will release our game with no retina support first, while watching whether it becomes popular or not. Might port it to support retina when the time comes then.

        Thanks anyway :)

  13. prerak says:

    I tried to run the demo code it works fine but when I tried to replace image peanutsHD.png in resources folder by some other image with same name peanutesHD.png
    now when i am trying to run this project in simulator it gives message
    (No launchable executable present at path)

    what should I do to run this demo again
    Please Help

    • Matt says:

      Unless that’s just a typo, you have an extra E in “peanutesHD.png”. That could be the issue?

      I’d actually recommend just using the newest version of Cocos2D instead of using this code, I think Ric has included a lot of this in the trunk now.

  14. gaurav says:

    hi , i have this code the image appears on the iphone but it is 2 times bigger, i want it to be half the size but double the pixel density which represents an HD image
    i have two images in my resources image.png and image-hd.png . i believe that cocos2d should automatically identify and properly place the images … Can some one explain what am i doing wrong ? Thanks

    [[CCDirector sharedDirector]enableRetinaDisplay:YES];
    CCSprite *mice = [CCSprite spriteWithFile:@"image.png"];
    //CGSize winSize = [[CCDirector sharedDirector]winSize];
    mice.position=ccp(40,40);
    [self addChild:mice];

  15. Chris says:

    Thanks for your time Matt. What about if you are not using Cocos2d? My app at the moment just uses @2x images, which don’t scale up when a user of an iPad uses the “2x” zoom mode. (Its a pretty simple app – see http://www.infusicalc.com – not anywhere in the realm of the games you make)

    How can I get my images (and apple’s font rendering) to look nice if I don’t use Cocos2d at all?

    • Matt says:

      Unfortunately, to my knowledge there’s no good way to get Apple’s font rendering to look good in 2x mode. In general, I don’t think this technique is possible with UIKit, although I could be wrong, I haven’t tried it too much.

  16. Pingback: detect iPad 2x button for iPhone app - Applerr.com All about Apple Products - Applerr.com All about Apple Products

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>