BIT-101 [2003-2017]

Cocos2d – 2D OpenGL for the iPhone made Easy. Part 1.


[NOTE: I checked the cocos2d source out through svn, which I discovered gives you version 0.8. If you download the source, it looks like the latest version available is 0.7.2. I’m not sure how many differences you’ll run into, but the first few comments below will show you at least one. But the solution is there as well.]

I’ve been working on iPhone dev obviously, and doing some games. I’ve fooled around with OpenGL a bit, but nothing serious. Been going through Jeff LaMarche’s OpenGL tutorials, which are fantastic. But for a lot of my purposes, I don’t really need the heavy 3D stuff. I really just need something that would do 2D with the power and speed of OpenGL. My last game, Vector Blocks was actually done fully in Cocoa, which I got away with because there’s really only a single object animating at any given time. For any more complex animation, Cocoa would pretty much die. The problem with OpenGL is it’s just so low level, you’d end up writing a ton of code just to get your game character and objects on the screen and moving around, even before you started to think about the actual mechanics of the game.

So I heard mention of cocos2d and went over to Google Code to check it out.

http://code.google.com/p/cocos2d-iphone/

I have to say, I’m pretty impressed. It is just what I was looking for. If you are a Flash / ActionScript developer coming into the iPhone world to make games, this is a dream come true. Basically, it gives you a bunch of objects that encapsulate drawing 2D textures via OpenGL. I shouldn’t have to say this, but cocos2d is NOT a 3D package. There are a few little 3D tricks in there, but for the most part, that “2d” in its name is there for a purpose.

To get started, go to the Google Code link above and download or check out the source. I’ve actually created an XCode template that will give you a cocos2d ready application. I say cocos2d because I didn’t actually include the library in the template. You’ll have to add that to the project yourself. That way I don’t have to worry about updating the template whenever the library changes. You can get the template here:

http://www.bit-101.com/iphone/Cocos2DReadyApplication.zip

Unzip that into your XCode templates directory, which is

/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Project Templates/Application/

Now you should be able to create a new project and choose a Cocos2D Ready Application.

There are instructions in the project, but the two steps you need to do to start coding are:

  1. Add the cocos2d classes to the project.
  2. Add the fps_image.png to the Resources group of the project.

For step 1, do NOT add the whole cocos2d folder you downloaded or checked out. Just add the cocos2d subdirectory. Later you might want to add additional packages such as cocoslive or the experimental stuff. But minimally you’ll need the cocos2d dir. For both of these steps, you can choose to either copy the files over into your project, or just access them from wherever you have them stored. It shouldn’t matter to XCode.

For step 2, this png can be found in the Resources/Images folder of the library.

If you’ve gotten this far, you’re ready to start writing some cocos2d. The first thing you need to know about is the Director. This is a well named object. It directs everything that happens. It’s a singleton, so you access it via [Director sharedDirector]. If you go to the main app delegate file in your project you’ll see some commented out code ready for you to implement. This sets up the Director for use.

[c][[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft];
[[Director sharedDirector] attachInWindow:window];[/c]

This is pretty obvious. It tells cocos2d that you want to run in landscape mode, and adds the director to the main application window so it can display stuff on the screen.

The next thing you need to know about are Scenes. Scene is another class in the library. You can think of it kind of like states or views in Flex view stack. At any one time you have a single scene running. You can display a scene by calling the runWithScene method of the Director. You can also push a scene onto the Director, which will save the current scene in a stack, or pop a scene off the Director’s stack, which will display the previously pushed stack. Or you can simply replace a scene with a new scene.

So you might have a splash or intro scene, a menu scene, a game scene or maybe multiple level scenes, a high score scene, etc. You just replace or push/pop each scene as needed. Like Flex states, you can use transitions to move between scenes.

So let’s make a scene! Right in the main app delegate class’s applicationDidFinish launching method, just uncomment the code in the template:

[c]- (void)applicationDidFinishLaunching:(UIApplication *)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setUserInteractionEnabled:YES];
[window setMultipleTouchEnabled:YES];

[[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft];
[[Director sharedDirector] attachInWindow:window];

[window makeKeyAndVisible];

Scene *scene = [Scene node];
[[Director sharedDirector] runWithScene:scene];
}
[/c]

Don’t forget to add the import statements for cocos2d.h and Scene.h. (These are actually in the template, just uncomment them.)

If you run or debug this now, you’ll see… NOTHING! Obviously, because we haven’t displayed anything. So let’s add something. A background image anyway.

You can easily add an image by creating a Sprite and calling addChild on the Scene. (See, I told you you Flash guys would like this!) First create a 480×320 pixel png file and add it to the Resources folder of your project. I called mine “background.png”. You can see it here:

background

Now change the last few lines of code at the end of the applicationDidFinishLaunching method to look like this:

[c]Scene *scene = [Scene node];
Sprite *background = [Sprite spriteWithFile:@“background.png”];
background.position = ccp(240, 160);
[scene addChild:background];
[[Director sharedDirector] runWithScene:scene];[/c]

What we’ve done is create a new Sprite using the background.png image, positioned it in the center of the screen, and added to the scene. A few things about the positioning. You see we are calling a function named “ccp”. This is nothing more than a macro to CGPointMake. Saves you a few key clicks. Also, notice that by default, a sprite’s “registration point” is in its geometric center. So when we set it to the center of the screen, it will be centered in the center, thus filling the whole screen. You can change the registration point via a property called anchor, but we don’t need to just yet.

So run this, and you’ll have a beautiful image like this:

background2

Er… what happened to my nice gradient? Well, you can go to the cocos2d mailing list archives and search “gradient” and you’ll see this is a known issue that has supposedly been fixed, but I sure still see it. What is supposed to fix it is adding this line as the first call to the Director:

[c][[Director sharedDirector] setPixelFormat:kRGBA8];[/c]

Unfortunately, this doesn’t help for me. I still get the ugly banding. Fortunately, I was able to hack into the source code with some hints from the mailing list and at least make a local fix. What I changed is in the Texture2d.m file, around line 193 in the current release. There’s a line that says:

pixelFormat = defaultAlphaPixelFormat;

If you change that to read:

pixelFormat = kTexture2DPixelFormat_RGBA8888;

Your gradients will work just fine. Now, I don't know what other repercussions this "fix" may have, so use it at your own risk. Or, research it some more, come up with a real fix and submit it to the team.

Disregard that crossed out stuff. I found a better way to handle this here.

Well, that's about it for now. Next installment, we'll actually make something move!

Oh, just in case there's any confusion, here's the whole applicationDidFinishLaunching method:

[c]- (void)applicationDidFinishLaunching:(UIApplication *)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setUserInteractionEnabled:YES];
[window setMultipleTouchEnabled:YES];

[[Director sharedDirector] setPixelFormat:kRGBA8];
[[Director sharedDirector] setDeviceOrientation:CCDeviceOrientationLandscapeLeft];
[[Director sharedDirector] attachInWindow:window];

[window makeKeyAndVisible];

Scene *scene = [Scene node];
Sprite *background = [Sprite spriteWithFile:@"background.png"];
background.position = ccp(240, 160);
[scene addChild:background];
[[Director sharedDirector] runWithScene:scene];
}[/c]

« Previous Post
Next Post »