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.

Posted in Cocos2D, iDevBlogADay, iOS Development | 29 Comments

Retinafy Your Game

This is the first of two posts on supporting high-res graphics in iOS games. This post will take a general approach to the subject, and the second post goes into into the specifics of implementing high-res graphics in Cocos2D.

Why support high-res graphics?

It wasn’t until I got my iPhone 4 that I really understood why I had to support the Retina display. I had assumed that Trainyard would look just as good on the iPhone 4 as it does on a regular iPhone, but unfortunately that’s not the case. 320×480 games look worse on the iPhone 4 than they do on normal phones. The scaling algorithm Apple uses to upscale low-res iPhone apps makes them look blurry. You’re not giving your game high-res graphics to make it look better, you’re giving it high-res graphics so that it doesn’t look worse.

You don’t have to support the Retina display, but if you want your games to look as good as possible, it’s something you should do. The other advantage to upgrading your graphics is that you can use the high-res images on the iPad as well, which means that even if your iPad app isn’t universal, it will still look great. Say goodbye to blurry 2x scale mode.

Retinafication considerations

You should be able to support high-res graphics without making any changes to the design and layout of your game. This isn’t like the difference between the iPhone and iPad. The iPhone and iPhone 4 have the exact same form factor. You need the graphics that look the same, just with more detail.

Don’t make your game based in 640×960 and then downscale it for older devices. I believe the “points” system Apple uses for UIKit is a good place to draw reference from. Keep all of your coordinates and positions in 320×480, and just scale the root of your display tree by a factor of two. If you’re using texture atlases (you should be), scale the coordinates when you load from the texture. In summary: scale your texture coordinates, not your display coordinates. Please, please don’t load high-res graphics on older devices. That’s just mean. You’ll need one set of low-res textures, and another set of high-res textures.

Supporting the iPad

I don’t believe an app should be marked “universal” unless its iPad mode is designed specifically for the iPad. With Trainyard, I didn’t made an iPad specific graphical layout, but I did want the game to use high-res graphics and look nice and crisp in 2x scale mode. What I wanted was definitely possible, but also a bit tricky.

Apple provides a handy system for loading high-res images with an @2x suffix, but unfortunately this doesn’t work on the iPad. For this reason and a few others, I recommend avoiding using @2x graphics (for now), and rolling your own high-res image loading. I use an “HD” suffix for all of my high-res images, and load them with a simple macro that checks whether the current device is an iPad or an iPhone 4.

You can treat the iPad and iPhone 4 in basically the exact same way, by loading high-res graphics and positioning them accordingly, and your game will look great in both 1x and 2x zoom. An interesting fact: Apple’s docs say that the UIScreen’s scale property is available in iOS 4.0 and up, but it’s actually available on the iPad as well, and it’ll tell you which “zoom level” you’re in.

Wrapping up

High-res graphics really will make your game look better. They can be a lot work, but if you implement them intelligently, the majority of the work will be on the graphical side of things, rather than coding. This post has been a very high-level overview, so if you have more in depth technical questions, please post a comment or message me on twitter(@MattRix). If you’re a Cocos2D developer, stay tuned for my in-depth post coming up later in the week.

PS: Yes, Retinafication actually is an official term, Apple uses it in this technote

Posted in iDevBlogADay, iOS Development | 1 Comment

Xcode Folder References

I decided to write about something a bit more technical for this week’s iDevBlogADay post: Xcode Folder References. They’re really quite simple, but I keep seeing projects that don’t use them when they should.

What are folder references?

There are two types of folders in Xcode: groups and folder references. You can use groups to organize files in your project without affecting their structure on the actual file system. This is great for code, because you’re only going to be working with your code in Xcode. On the other hand, groups aren’t very good for resource files.

On any reasonably complicated project, you’ll usually be dealing with dozens – if not hundreds – of asset files, and those assets will need to be modified and manipulated from outside of Xcode, either by you or a designer. Putting all of your resource files in one flat folder is a recipe for disaster. This is where folder references come in. They allow you to organize your files into folders on your file system and keep that same folder structure in Xcode.

Creating folder references

You can create folder references by ticking the “Create Folder References for any added folders” radio button when you’re adding files to your project.

You’ll now see little blue folders in your project instead of yellow folders. The blue folders are folder references. Pretty simple.

Using folder references in code

Unlike with groups, when you use folder references you have to specify the full path to the file. If you’re using UIImage’s imageNamed, this is really easy:

//
//
UIImage *image = [UIImage imageNamed:@"Cats/Dogs/someImage.png"];
//
//

Unfortunately, you usually won’t be using imageNamed, you’ll be using imageWithContentsOfFile, or some other method that requires a full file path, so things get a tiny bit more complicated. You’ll have to get the path of the file from the bundle:

//
//
NSString *fullPath = [[NSBundle mainBundle] pathForResource:@"someImageFile.png" ofType:nil inDirectory:@"Cats/Dogs"];
UIImage *image = [UIImage imageWithContentsOfFile:fullPath];
//
//

Ok, that works, but wouldn’t it be nice if we didn’t have to split the path into two separate strings? Luckily Apple has provided a couple methods for doing exactly that in the (NSStringPathExtensions) category: lastPathComponent and stringByDeletingLastPathComponent

//
//
NSString *imagePath = @"Cats/Dogs/someImageFile.png";
NSString *fullPath = [[NSBundle mainBundle] pathForResource:[imagePath lastPathComponent] ofType:nil inDirectory:[imagePath stringByDeletingLastPathComponent]];
UIImage *image = [UIImage imageWithContentsOfFile:fullPath];
//
//

Great! But now our code has become really long winded, so let’s simplify this with a macro(you could also use a C function, a class method, or even a category on NSString).

//
//
#define GetFullPath(_filePath_) [[NSBundle mainBundle] pathForResource:[_filePath_ lastPathComponent] ofType:nil inDirectory:[_filePath_ stringByDeletingLastPathComponent]]
//
//
UIImage *image = [UIImage imageWithContentsOfFile:GetFullPath(@"Cats/Dogs/someImageFile.png")];
//
//

Anything else?

Yes! When you’re using folder references, you usually have to do a clean every time you do a build if you’ve changed any of the assets. In the process of writing this blog post, I stumbled upon this article on folder references by @MajicDave. Our posts cover some similar ground, but his goes into detail about how to solve this issue so that you no longer have to do a clean. Very handy.


Posted in iDevBlogADay, iOS Development, Workflow | 11 Comments

Pricing Games

A little debate on App Store game pricing started today in the comments for a TouchArcade post on Solipskier.

One user stated his opinion:

“I’m not saying it’s a bad game, quite the contrary, I have only positive things to say. Just remember, it is not by any means worth $3. $1 would be the most appropriate.”

The classic response to this complaint is that “it costs you less than a good cup of coffee”. Let’s clear something up: comparing the price of physical goods to digital goods is pretty silly. Physical goods have reproduction costs, while digital goods don’t. What this means is that it’s ok to sell your game to lots of people for cheap if your total profit is still high. There seems to be a feeling that pricing games at $0.99 somehow cheapens the entire industry. I firmly believe that a $0.99 game is not a “worthless” game, it’s just a game with mass market appeal. This is the basis of Marco Arment’s “Two App Stores” theory.

I’ve heard it said that the App Store’s “race to the bottom” will ruin users’ perceptions of game prices. I really don’t think that’s true at all. For a long time there’s been a place with way lower prices on games: the web. The Flash version of Canabalt is completely free, and it’s identical to its iOS counterpart, yet that didn’t stop people from buying Canabalt on their iOS devices. The context of where you’re buying games matters. I wouldn’t think twice about buying a game for $15 on Steam, but I would never spend $15 on a game on the App Store. And don’t worry, indie games are still thriving elsewhere, despite low prices on iTunes, just take a look at the example of the Humble Indie Bundle.

$2.99 is not a ridiculous price for a game like Solipskier. I’m having a lot of fun with it, and I’m sure the developers put a lot of work and effort into the game. That being said, compared to other games at the $2.99 price point, like Plants vs Zombies, Chopper 2 and Osmos, the price starts to seem a tad high.  You could argue that Plants vs Zombies, Chopper 2 and Osmos are underpriced, but there are dozens more games out there just as good as them at that price. That’s my point: we’re dealing with a strange market where everything is underpriced. And if everything is underpriced, well, maybe we need to change our definition of what underpriced is.

Posted in App Store | 4 Comments

Trains on Paper

When I started coming up with ideas for my first iDevBlogADay post, I considered writing about everything from procrastination to Xcode folder references. It took a couple helpful tweets to make me realize I should write about something I really know about: the process of developing Trainyard.

Trainyard is my iPhone game. It’s a grid-based logic puzzle game where the goal is to get each train from its initial station to a goal station. Every train starts out a certain colour, and most puzzles require the player to mix and merge trains together so that the correctly coloured trains end up at the right stations. The player’s only method of input is drawing tracks for the trains to follow. It’s kind of hard to explain with words, so if you’re confused you should watch the trailer.

I came up with the idea for the game a year and a half ago, during a period where I was actually working on ideas for Flash games. At that time I didn’t have any iPhone development experience, and had barely even looked into Objective-C. I did, however, have an hour long commute by train which gave me plenty of time to come up with interesting game ideas. My main game prototyping tools were (and still are) a notepad and an Acer Asprire One netbook. Yes, a netbook. I’ll probably write more about that little guy in a future post. For now let’s focus on the notepad.

Every game designer should carry around a notepad as much as possible. You never know when the spark of a great game idea is going to come to you. I went through notepads and notebooks of all shapes and sizes while working on Trainyard, so I really don’t think it matters which kind you use as long as you’ve got one. I suppose you could try an iPhone app for recording ideas, but there’s something special about being able to sketch with a pen.

A notepad is more than a place to write down the gist of an idea, it’s also a great place to flesh concepts out and figure out how you’re going to execute them. In the case of Trainyard, my initial idea (as you can see on the page above) was “each train starts with a solid colour that they have to mix and deliver”. So far so good. Ok, let’s look a little further down that page: “[trains] can collide, have two cars, and pick up paint”. That’s quite different. My original idea was that each train had several cars to pull, and each train would also have its own control panel where you could adjust the speed before pressing “Go”. Confusing? Yes. Would it make for a good game? Maybe, but it’s definitely not Trainyard.

On this second page you can see some more questionable ideas: “Game could be hex based instead of square”, “limitations include fixed quantities of pieces”.  At this point in the process I was thinking of limiting the user’s selection of pieces like a lot of other similar puzzle games do. After thinking through the mechanic over the next few days, I realized that I could make it work without limiting the user’s piece selection. I think the flexible unlimited-track nature of Trainyard is now something that really makes it unique. At one point a lot later in the prototype process I even made a hex based prototype (flash link)  just for fun, but it felt weird and confusing.

After those first few sketches, I worked over the course of a few days to eliminate most of the extraneous ideas and the concept gradually got closer and closer to what it is today. About a week after coming up with the initial idea the core gameplay mechanic was transformed into something nearly identical to what it is now. Keep in mind that at that point I still hadn’t coded anything; it was all on paper. I even have a sheet of paper with a detailed solution to the very first Trainyard puzzle ever created, which actually made it into the game as The Original. Unfortunately I couldn’t find that piece of paper in time for this post, but when I do I’ll be sure to put it here.

The moral of the story: as developers, we’re often tempted to go straight to our computers to make prototypes and tests, but sometimes it’s good to just sit down with a pen and paper and flesh out an idea.

I hope you enjoyed this first little look into the Trainyard development process. I’m planning to post at least a couple times a week, so please add http://struct.ca/feed/ to your RSS readers and whatnot. I’ve never had my own blog, so my writing style is going to be a bit rough for a while, but bear with me, it’ll get better over time :) If you didn’t come here from iDevBlogADay, make sure you check all those other blogs out, there’s some great stuff there.

Posted in Game Design, iDevBlogADay, Trainyard, Workflow | 8 Comments