Chapter 2

Getting Started

I will get you up to speed and developing cocos2d games as quickly as possible. By the end of this chapter, you’ll be able to create new cocos2d projects based on the supplied Xcode project templates. I’ll also introduce you to the important bits of knowledge you need to keep in mind during game development. And since it’s always been a big source of confusion, I’ll explain how memory management works in the context of cocos2d, ideally helping you avoid some of the common pitfalls. At the end of this chapter, you’ll have a first cocos2d project based on a project template up and running.

What You Need to Get Started

In this section, I’ll quickly walk you through the requirements and necessary steps to get started. Getting registered as an iOS developer and creating the necessary provisioning profiles are both excellently documented by Apple, so I won’t re-create that detailed information here.

System Requirements

These are the minimum hardware and software requirements for developing iOS applications:

  • Intel-based Mac computer with 1GB RAM
  • Mac OS X 10.6 (Snow Leopard) or greater
  • Any iOS device

For development, any Intel-based Mac computer suffices. Even the Mac mini and MacBook Air are perfectly fine for developing iOS applications and games. I do recommend having 2GB of RAM installed. It’ll make using your computer smoother, especially since game development tools often require much more memory than most other software. You’ll be handling a lot of images, audio files, and program code, and you’ll probably be running all these tools in parallel.

Note that Mac OS X 10.6 is mandatory for iOS development since the release of the iOS SDK 4 in June 2010. With the release of Mac OS X 10.7 Lion, you can expect that eventually it will become a requirement for iOS development. As a developer with Apple, you’re regularly required to be using the latest OS X version.

If you are running an older version of Mac OS X, please consult the Max OS X Technical Specifications web site (www.apple.com/macosx/specs.html) to learn whether your Mac meets the system requirements and how to purchase and upgrade to the latest Mac OS X version.

Register as an iOS Developer

If you haven’t done so yet, you might want to register yourself as an iOS developer with Apple. Access to the iOS Developer Program costs $99 per year. If you plan to submit Mac OS X apps to the Mac App Store, you will also have to register as a Mac OS X developer, which costs an additional $99 per year.

As a registered developer, you get access to the iOS SDK, Xcode, and the iOS Developer Portal where you have to set up your development devices and provisioning profiles in order to deploy your app to iOS devices. You also get access to iTunes Connect where you can manage your contracts, manage and submit your apps, and review financial reports. In addition, you’ll be offered preview (beta) versions of Apple software. Registered Mac OS X developers will also get free access to the latest Mac OS.

You can register as an iOS developer at http://developer.apple.com/programs/ios.

To register as a Mac OS X developer, go to http://developer.apple.com/programs/mac.

TIP: You can also get Xcode from the Mac App Store for free. This download includes the iOS SDK and Mac OS X SDK. You’ll be able to develop cocos2d apps for iOS and Mac OS X. The catch: you cannot run your iOS apps on an iOS device unless you register as an iOS developer, so you’ll be limited to using the iOS Simulator. You will also not be able to submit your app to the iOS App Store or Mac App Store until you sign up as a registered iOS respectively Mac OS developer.

Certificates and Provisioning Profiles

Eventually you’ll want to deploy the games you’re building onto your iOS device. To do so, you must create an iOS development certificate, register your iOS device, and enable it for development. Finally, you’ll create development or distribution provisioning profiles, download them to your computer, and set up each Xcode project to use them.

All of these steps are well explained on the iOS Provisioning Portal. Apple has done an excellent job at documenting these steps on the How To tabs of each section of the Provisioning Portal.

The iOS Provisioning Portal is accessible for registered iOS developers at http://developer.apple.com/ios/manage/overview/index.action.

Download and Install the iOS SDK

As a registered iOS developer, you can download the latest iOS SDK from the iOS Dev Center. The download is well over 4GB and will take a while to download and install, so you might want to do it right away.

After the installation of the iOS SDK is complete, you are set with everything you need to develop iOS applications, including the Xcode integrated development environment (IDE). If you’ve never worked with Xcode before, I suggest you familiarize yourself with it. I recommend Learn Xcode Tools for Mac OS X and iPhone Development by Ian Piper (Apress, 2010).

CAUTION: It may be tempting to be at the bleeding edge of iOS SDK development. From time to time, beta versions of the iOS SDK are made available. I recommend not using iOS SDK beta versions unless you have a very, very good reason to do so!

Beta versions can contain bugs, they may be incompatible with the current cocos2d version, and they are under NDA. This means it’s hard to find solutions if any issue related to the beta version arises, since no one is allowed to discuss the beta SDK in public.

Moreover, you have to install a beta version of the iOS to your device, and you can’t revert to a previous iOS version. Installed apps on your device may be incompatible with the new iOS beta, and they usually aren’t updated until the new iOS SDK is officially released. If you rely on any apps to do your work, don’t upgrade.

Download and Install cocos2d

The next step is to get cocos2d. You can download it from www.cocos2d-iphone.org/download. Since many new developers continue to have issues with the template installation script, I also provide an installer for cocos2d and cocos3d that runs the template installation script for you. You can download the installer on Cocos2d Central: http://cocos2d-central.com/files/file/6-installer.

I recommend downloading and extracting the Stable version of cocos2d. The Unstable version doesn’t mean it’s going to crash all the time, but it is a beta version. It’ll work just fine in general, but it may have some rough edges, untested features, and incompatibilities with third-party tools. Before you consider the Unstable version, please review the release notes to see whether it contains anything of particular use to you. If not, just stick to the Stable version.

After downloading and extracting cocos2d, you’ll have a subfolder named cocos2d-iphone-1.0.1 or similar, depending on the exact version number of cocos2d you downloaded.

Install cocos2d Xcode Project Templates

NOTE: If you used my cocos2d/cocos3d installer, you can skip this section. The Xcode project templates have already been installed by the installer.

Open the Terminal app, which you’ll find in the Utilities folder of your Applications folder on your Mac. Or just enter Terminal.app in Spotlight to locate it. The cocos2d Xcode project templates installation procedure is driven by a shell script that you’ll have to run from the command-line program Terminal.

First, change to the directory where cocos2d is installed. For example, if your cocos2d version is installed under Documents in the folder cocos2d-iphone-1.0.0, then enter the following:

cd ~/Documents/cocos2d-iphone-1.0.0

Press Return to change into the cocos2d directory and then enter the following:

./install-templates.sh –f -u

Press Return to run the template installation script. If everything goes fine, you should see a number of lines printed on the Terminal window. Most of them will start with “…copying.” If that’s the case, the templates should now be installed.

If you get any kind of error, verify that you have changed into the cocos2d-iphone directory with the cd command and that the command for the install-templates.sh script is correct, including spaces between the command and the –f and –u options. If that does not help, consider using the cocos2d installer I created to alleviate exactly such dreaded template installation problems. You can download the installer from here:

cocos2d-central.com/files/file/6-installer
Create a cocos2d Application

Now open Xcode and select File images New Project. Under User Templates you should see the cocos2d project templates, as shown in Figure 2–1.

NOTE: The Box2d and Chipmunk application templates will be discussed in Chapter 13. Feel free to try them if you want to have some fun with physics right now.

images

Figure 2–1. The cocos2d Xcode project templates

Choose the cocos2d Application template and name it HelloWorld.

TIP: It is good practice not to use space characters in project names. Xcode doesn’t mind, but some tools you might use could. It’s just a matter of defensively avoiding any potential hiccups.

For a very, very long time, programmers who built operating systems and applications could rely on file names not containing spaces. Even today, after modern operating systems have allowed spaces in file names for at least the past 10 years, there are occasional problems related to spaces and special characters in file names. I always avoid naming anything code-related, whether projects, source files, or resources, with spaces or other special characters. Only numbers, digits, and the minus sign and underscore are guaranteed safe for developers to use in file names.

Xcode will create the project based on the template. An Xcode project window like the one in Figure 2–2 will open.

images

Figure 2–2. The newly created HelloWorld project in Xcode 4

When you click the Run button, the project will build and then run in the iOS Simulator. The result should look like Figure 2–3.

images

Figure 2–3. Success! The template project works and displays a “Hello World” label running in the iPhone Simulator.

The HelloWorld Application

So here we are—with minimal fuss you created a running cocos2d application. Perfect. Say no more. Say no more.

But now you want to know how it works, right? Well, I didn’t expect you’d let me off the hook so easily. And something tells me that, however deep I go into the details over the course of the book, you’ll want to know more. That’s the spirit!

Let’s check what’s in the HelloWorld code project and see how it all works so you get a rough overview of how things are connected. Feel free to play around with this HelloWorld project. If anything breaks, you can just start over with a new project created from the cocos2d template. Over the course of this book, you’ll learn all the details about cocos2d and the settings first mentioned in this chapter.

Locating the HelloWorld Files

First, here’s a quick primer in case you’ve never worked with Xcode before. By default, you’ll see a pane called Project Navigatoron the left side of the Xcode project window, like the one in Figure 2–4. That’s where Xcode keeps all file references, among plenty of other things like targets and executables. Just focus on the groups and files for now that are below the HelloWorld project.

images

Figure 2–4. Xcode’s Project Navigator pane. The expanded groups contain the project files we’ll be looking at.

In the group named cocos2d you’ll find all the files the cocos2d game engine consists of. Feel free to explore these files if you’re curious, but unless you know what you’re doing, you should not modify them. The same goes with the other cocos2d-related groups; they are the groups that are not expanded in Figure 2–4.

You don’t need to know the details of the cocos2d game engine, but it’s good to have the source files accessible, especially when it comes to debugging or in case you get curious and want to know how things work under the hood and learn a trick or two.

CAUTION: Xcode’s Project Navigatorpane looks a lot like folders and files in Finder. Don’t mistake what Xcode calls groups for Finder’s folders. You can have your files in Xcode arranged in many groups, but in Finder they can and normally will still all be in the same folder. This is why they are not called folders but groups. They allow you to arrange files logically without affecting their physical location on your computer’s hard drive.

Resources

In the Resources group you’ll find (and later add) all the additional files that aren’t source code, such as images and audio files.

The Default.png file is the image that’s displayed when iOS is loading your app, and Icon.png is, of course, the app’s icon. The fps_images.png file is used by cocos2d to display the framerate; you should not remove or modify it.

Inside the Info.plist file you’ll find a number of settings for your application. You’ll only need to make changes here when you get close to publishing your app.

Supporting Files

If you’re familiar with programming in C or similar languages, you may recognize main.m in the Other Sources group as the starting point of the application. The only other file in this group is the precompiled header file Prefix.pch.

Main.m

Everything that happens between the main function and the HelloWorldAppDelegate class is the behind-the-scenes magic of the iOS SDK, over which you have no control. Since you’ll hardly ever need to change main.m, you can safely ignore its contents. Still, it never hurts to peek inside.

To quickly sum up, the main function creates an NSAutoreleasePool and then calls UIApplicationMain to start the application using HelloWorldAppDelegate as the class that implements the UIApplicationDelegate protocol.

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    int retVal = UIApplicationMain(argc, argv, nil, images
        @"HelloWorldAppDelegate");
    [pool release];
    return retVal;
}

The only interesting point to take away from this is that every iOS application uses an NSAutoreleasePool to help you manage memory. In short, by using the autorelease message on objects, you don’t have to worry about sending them a release message. The autorelease pool ensures that the memory of autorelease objects is eventually released. Don’t worry if you don’t know what the heck I’m talking about here. I’ll introduce you to memory management with cocos2d later in this chapter.

You can also learn more about using autorelease pools in Apple’s Memory Management Programming Guide at http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html.

Precompiled Prefix Header

Just in case you’re wondering what the Prefix.pch header file is for, it’s a tool used to speed up compilation. You are supposed to add the header files of frameworks that never or only rarely change to the prefix header. This causes the framework’s code to be compiled in advance and made available to all your classes. Unfortunately, it also has the disadvantage that, if a header added to the prefix header changes, all your code will recompile, which is why you should only add header files that rarely or never change.

For example, the cocos2d.h header file is a good candidate to add to the prefix header, as I’ve done in Listing 2–1. To create a noticeable increase in compilation time, your project would need to be reasonably complex, however, so don’t get your stopwatch out just yet. But it’s good practice to add cocos2d.h as a prefix header right away, if only to never have to write #import "cocos2d.h" in any of your source files again.

Listing 2–1. Adding the cocos2d.h Header File to the Prefix Header

#ifdef __OBJC__
    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    #import "cocos2d.h"
#endif

Once again, you can refer to Apple’s developer documentation if you want to learn more about reducing build times with prefix headers: http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/XcodeBuildSystem/800-Reducing_Build_Times/bs_speed_up_build.html.

HelloWorld Classes

Two classes make up the core of the HelloWorld project. The AppDelegate class handles the application’s global events and state changes, while the HelloWorldLayer class contains all the code that displays the “Hello World” label.

AppDelegate

Every iOS application has one AppDelegate class that implements the UIApplicationDelegate protocol. In our HelloWorld project, it’s simply called AppDelegate.

The AppDelegate is a global concept you’ll find in every iOS application. It is used to track state changes of the application, and to do this it receives messages from the iOS at certain points in time. For example, it allows you to determine when the user gets an incoming phone call or when the application is low on memory. The very first message your application will receive is the applicationDidFinishLaunching method. That’s where all the startup code goes and where cocos2d is initialized.

If you want to learn more about the AppDelegate’s various methods, what they do, and when these messages are sent by the iOS SDK, you can look it up in Apple’s reference documentation on the UIApplicationDelegate protocol at http://developer.apple.com/iphone/library/documentation/uikit/reference/UIApplicationDelegate_Protocol.

NOTE: Since I’m talking about application startup, I might as well talk about application shutdown. You may eventually notice an oddity with the AppDelegate’s dealloc method. It never gets called! Any breakpoint set in the AppDelegate’s dealloc method will never be hit!

This is normal behavior. When iOS terminates an application, it simply wipes the memory clean to speed up the shutdown process. That’s why any code inside the dealloc method of the AppDelegate class is never run. Also, it’s bad practice to call dealloc manually, so don’t try to “fix” this issue by doing so. If you ever need to run code in your AppDelegate just before the application terminates, do it inside the applicationWillTerminate method.

In most cases, there are only three things you might want to change during the cocos2d initialization process:

[[CCDirector sharedDirector] images
    setDeviceOrientation:kCCDeviceOrientationLandscapeLeft];
[[CCDirector sharedDirector] setAnimationInterval:1.0/60];
[[CCDirector sharedDirector] setDisplayFPS:YES];

I’ll provide a few details on each of these in the following sections.

Device Orientation

The most important one is setting the device orientation. The HelloWorld application uses a landscape orientation, which means you’ll be holding your iOS device sideways. If you change this option from kCCDeviceOrientationLandscapeLeft to kCCDeviceOrientationLandscapeRight, you’ll find that the message “Hello World” is now displayed upside down compared to what it was before.

Here’s a list of supported device orientations. Try each of them to see how they change the “Hello World” text label orientation.

  • kCCDeviceOrientationPortrait
  • kCCDeviceOrientationPortraitUpsideDown
  • kCCDeviceOrientationLandscapeLeft
  • kCCDeviceOrientationLandscapeRight

TIP: You can change the device orientation at a later point in time, even during game play. For example, you can make this a game setting the user can choose. As long as you change from one landscape mode to another or from one portrait mode to another, you don’t even need to modify your code. Allowing the user to choose to play the game in any of the two landscape or portrait directions is very straightforward to implement. Since everyone has a different opinion on that matter, it’s a good idea to let the user choose between the regular and the upside-down orientation. Please refer to Chapter 15, which explains how to implement autorotation to the current device orientation.

Animation Interval

The animation interval determines how often cocos2d updates the screen. Effectively, this affects the maximum framerate your game can achieve.

The animation interval is not given in frames per second, however. It’s the inverse because it determines how frequently cocos2d should update the screen. That’s why the parameter is 1.0/60—because 1 divided by 60 results in 0.0167, which is the time in seconds between each call to update the screen. Of course, if your game is complex, it might take the CPU or GPU longer than 0.0167 seconds to display a frame, so there’s no guarantee that 60 fps will be achieved throughout. As a matter of fact, it’s your responsibility to keep the game running at a high framerate. I’ll explain techniques to improve performance throughout the book.

In some cases, it may be preferable to lock the framerate to 30 frames per second. This may be helpful in very complex games where you can’t achieve 60 fps consistently and the framerate fluctuates a lot between 30 and 60 fps. In such a case, it’s often better to lock the framerate to the lowest common denominator because a lower but steady framerate is perceived as smoother by players than a framerate that tends to fluctuate abruptly, even when the actual average framerate may be higher. Human perception is a tricky thing.

NOTE: You can’t render more than 60 frames per second on iOS devices. The device’s display is locked to update at 60 frames per second (Hz), and forcing cocos2d to render more frames than 60 per second is, at best, not doing anything. At worst, it can actually slow down your framerate. Stick with the animationInterval of 1.0/60 if you want to run cocos2d at the fastest possible rate.

Display FPS

Enabling the FPS display will show a small number at the lower-left corner of the screen. This is your framerate, or the frames per second the screen is updated. Ideally, your game should be running at 60 fps at all times, especially if it’s an action or twitch-based game. Some games, such as most puzzle games, can go with a constant 30 fps just fine. The FPS display helps you keep track of the framerate and any hiccups or stutters your game is experiencing.

NOTE: If you need to tweak the responsiveness of the FPS display, you can do so by modifying the CC_DIRECTOR_FPS_INTERVAL line in ccConfig.h. You’ll find this file in the cocos2d Sources/cocos2d group. By default it is set to 0.1, which means the framerate display will be updated ten times per second. If you increase the value, the FPS display will average out over a longer period of time. However, you won’t be able to see any sudden, short drops in framerate, which can still be noticeable. Keep that in mind.

HelloWorldLayer

The HelloWorldLayer class is where pure cocos2d code does its magic to display the “Hello World” label. Before I get into that, you should understand that cocos2d uses a hierarchy of CCNode objects to determine what is displayed where.

The base class of all nodes is the CCNode class, which contains a position but has no visual representation. It’s the parent class for all other node classes, including the two most fundamental ones: CCScene and CCLayer.

CCScene is an abstract concept and does nothing more than to allow proper placement of objects in the scene according to their pixel coordinates. A CCScene node is thus always used as the parent object for every cocos2d scene hierarchy. Most of the time you will have only one running scene, except when transitioning from one scene to another.

The CCLayer class does very little by itself other than allowing touch and accelerometer input. You’ll normally use it as the first class added to the CCScene, simply because most games use at least simple touch input.

If you open the HelloWorldLayer.h header file, you’ll see that the HelloWorldLayer class is derived from CCLayer. So, where does the CCScene class come into play?

Since CCScene is merely an abstract concept, the default way of setting up a scene has always been to use a static initializer +(id) scene in your class. This method creates a regular CCScene object and then adds an instance of the HelloWorldLayer class to the scene. In almost all cases, that’s the only place where a CCScene is created and used. The following is a generic example of the +(id) scene method:

+(id) scene
{
    CCScene *scene = [CCScene node];
    id layer = [HelloWorldLayer node];
    [scene addChild:layer];

    return scene;
}

First, a CCScene object is created using the static initializer +(id) node of the CCScene class. Next, our HelloWorldLayer class is created using the same +(id) node method and then added to the scene. The scene is then returned to the caller.

Moving on to the –(id) init method in Listing 2–2, you’ll notice something that might seem odd: self is assigned the return value from the init message sent to the super object in the call to self = [super init]. If you come from a C++ background, you’ll shudder in pain looking at this. Don’t get too upset; it’s all right. It simply means that in Objective-C we have to manually call the superclass’s init method. There is no automatic call to the parent class. And we do have to assign self the return value of the [super init] message because it might return nil.

Listing 2–2. The init Method Creates and Adds the “Hello World” Label

-(id) init
{
    if ((self = [super init])) {
        // create and initialize a label
        CCLabelTTF* label = [CCLabelTTF labelWithString:@"Hello World"
                                               fontName:@"Marker Felt"
                                               fontSize:64];

        // get the window (screen) size from CCDirector
        CGSize size = [[CCDirector sharedDirector] winSize];

        // position the label at the center of the screen
        label.position = CGPointMake(size.width / 2, size.height / 2);

        // add the label as a child to this Layer
        [self addChild:label];
    }
    return self;
}

The CCLabelTTF class draws text on the screen using a TrueType font.

If you’re deeply concerned by the way Objective-C programmers write the call to [super init], here’s an alternative that might ease your mind. It’s fundamentally the same, just not what tradition dictates:

-(id) init
{
    self = [super init];
    if (self != nil) {
        // do init stuff here …
    }
    return self;
}

Now let me explain how the label is added to the scene. If you look again at the init method in Listing 2–2, you’ll see that a CCLabelTTF object is created using one of init’s static initializer methods. It’ll return a new instance of the CCLabelTTF class as an autoreleased object. To not have its memory freed after control leaves the init method, you have to add the label to self as a child using the [self addChild:label] message. In between, the label is assigned a position at the center of the screen. Note that whether you assign the position before or after the call to addChild doesn’t matter.

Memory Management with cocos2d

At this point, I need to talk a bit about memory management and the autorelease message. Memory management in the reference-counting Objective-C world is governed by two simple rules:

  • If you own (alloc, copy, or retain) an object, you must release or autorelease it later.
  • If you send autorelease to an object, you must not release it.

Normally, when you create an object in Objective-C, you do so by calling alloc. By doing this, you become responsible for releasing the object when you don’t need it anymore. The following is a typical alloc/init and release cycle:

// allocate a new instance of NSObject
NSObject* myObject = [[NSObject alloc] init];

// do something with myObject here …

// release the memory used by myObject
// if you don't release it, the object is leaked
// and the memory used by it is never freed.
[myObject release];

Now, with the autorelease message and the fact that iOS applications always use an autorelease pool, you can avoid sending the release message. Here’s the same example rewritten using autorelease:

// allocate a new instance of NSObject
NSObject* myObject = [[[NSObject alloc] init] autorelease];

// do something with myObject here …

// no need to call release, in fact you should not send release as it would crash.

As you can see, this simplifies memory management somewhat in that you no longer have to remember to send the release message. The autorelease pool takes care of that for you by sending the object a release message at the end of the current frame update if it is no longer referenced. Creating the object gets just a little bit more complex because the autorelease message was added.

Now consider the following code, which illustrates how you’d allocate a CCNode object if you followed the traditional release style:

// allocate a new instance of CCNode
CCNode* myNode = [[CCNode alloc] init];

// do something with myNode …

[myNode release];

This is not the preferred way to create cocos2d objects. It’s much easier to use the static initializer methods, which return an autorelease object. Contrary to what Apple recommends, the use of autorelease is consistently built into the cocos2d engine’s design by moving calls like [[[NSObject alloc] init] autorelease] to a static method in the class itself. And that’s a good thing—do not fight it! It will make your life easier since you don’t have to remember which objects need to be released, which is often cause for either crashes due to over-releasing certain objects or memory leaks due to not releasing all objects.

In the case of the CCNode class, the static initializer is +(id) node. The following code sends the alloc message to self, which is equivalent to using [CCNode alloc] if the code is placed in the CCNode implementation.

+(id) node
{
    return [[[self alloc] init] autorelease];
}

It’s just a little more generic to use self in this case and perfectly legal in case you happen to be a C++ programmer now scratching your head.

Seeing this, we can rewrite the CCNode allocation to use the static initializer, and, quite unsurprisingly, the code is now very short, concise, and tidy. Just the way I like it:

// allocate a new instance of CCNode
CCNode* myNode = [CCNode node];

// do something with myNode …

That’s the beauty of using autorelease objects. You don’t need to remember to send them a release message. Each time cocos2d advances to the next frame, the autorelease objects that are no longer in use are released automatically. But there’s also one caveat. If you use this code and at least one frame later you want to access the myNode object, it’ll be gone. Sending any messages to it will cause an EXC_BAD_ACCESS crash.

Simply adding the CCNode* myNode variable as a member to your class doesn’t mean that the memory used by the object is automatically retained. If you want an autorelease object to stick around into the next and future frames, you do need to retain it and subsequently release it if you don’t explicitly add it as a child node.

There’s an even better way to use autorelease objects and keep them around without explicitly calling retain. Usually you’ll create CCNode objects and add them to the scene hierarchy by adding the nodes as children to another CCNode-derived object. You can even get rid of the member variable if you want to, by relying on cocos2d to store the object for you.

// creating an autorelease instance of CCNode
-(void) init
{
    myNode = [CCNode node];
    myNode.tag = 123;

    // adding the node as children to self
    [self addChild:myNode];
}

-(void) update:(ccTime)delta
{
    // later access and use the myNode object again
    CCNode* myNode = [self getChildByTag:123];

    // do something with myNode
}

The magic is that addChild adds the object to a collection, in this case a CCArray that’s similar to the NSMutableArray of the iOS SDK but faster. The CCArray and the NSMutableArray and any other iOS SDK collection automatically send a retain message to any object added to them and send a release message to any object removed from the collection. Thus, the object stays around and remains valid and can be accessed at a later time, yet it will automatically be released after it has been removed from the collection.

What you should keep in mind is that managing memory for cocos2d objects is best done as I described here. You may run into other developers who say that autorelease is bad or slow and shouldn’t be used. Don’t give in to them.

NOTE: The Apple developer documentation recommends reducing the number of autorelease objects. Most cocos2d objects, however, are created as autorelease objects. It makes memory management much easier, as I’ve shown.

If you start using alloc/init and release for every cocos2d object, you’ll get yourself into a lot of pain for little to no benefit. That isn’t to say that you’ll never use alloc/init; it does have its uses and is sometimes even required. But for cocos2d objects, you should rely on using the static autorelease initializers.

Autorelease objects have only one caveat, which is that their memory is in use until the game advances by one frame. This means if you create a lot of throwaway autorelease objects every frame, you might be wasting memory. But that’s actually a rare occurrence.

This concludes my quick primer on cocos2d memory management. For a more in-depth discussion of memory management, refer to Apple’s Memory Management Programming Guide (http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html).

Changing the World

What good is a template project like HelloWorld if I don’t have you tinker with it at least a little? I’ll have you change the world by touching it! How’s that for a start?

First you’ll make two changes to the init method to enable touch input and to use a tag value to retrieve the label at a later point. The changes are highlighted in Listing 2–3.

Listing 2–3. Enabling Touch and Gaining Access to the Label Object

-(id) init
{
    if ((self = [super init])) {
        // create and initialize a label
        CCLabelTTF* label = [CCLabelTTF labelWithString:@"Hello World"
                                               fontName:@"Marker Felt"
                                               fontSize:64];

        // get the window (screen) size from CCDirector
        CGSize size = [[CCDirector sharedDirector] winSize];

        // position the label at the center of the screen
        label.position = CGPointMake(size.width / 2, size.height / 2);

        // add the label as a child to this Layer
        [self addChild: label];

        // our label needs a tag so we can find it later on
        // you can pick any arbitrary number
        label.tag = 13;

        // must be enabled if you want to receive touch events!
        self.isTouchEnabled = YES;
    }
    return self;
}

The label object gets 13 assigned to its tag property. Now why did you do that? I know, I told you to, but I must have had a reason, right? In the previous section, I explained that’s how you can later access a child object of your class—you can refer to it by its tag. The tag number is completely arbitrary, other than that it must be a positive number and every object should have its own tag number, so there aren’t two with the same number or you couldn’t tell which you’d be retrieving.

TIP: Instead of using magic numbers like 13 as tag values, you should get in the habit of defining constants to use with tags. You’ll have a hard time remembering what tag number 13 stands for, compared to writing a meaningful variable name like kTagForLabel. I’ll get to this in Chapter 5.

In addition, self.isTouchEnabled is set to YES. This is a property of the CCLayer class and tells it that you want to receive touch messages. Only then will the method ccTouchesBegan be called:

-(void) ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
    CCLabelTTF* label = (CCLabelTTF*)[self getChildByTag:13];
    label.scale = CCRANDOM_0_1();
}

By using [self getChildByTag:13], you can access the CCLabelTTF object by its tag property, which you assigned in the init method. You can then use the label as usual. In this case, we use cocos2d’s handy CCRANDOM_0_1() macro to change the label’s scale property to a value between 0 and 1. This will change the label’s size every time you touch the screen.

Since getChildByTag will always return the label, we can safely cast it to a (CCLabelTTF*) object. However, you should be aware that doing so will crash your game if the retrieved object is not derived from the CCLabelTTF class for some reason. This could easily happen if you accidentally give another object the same tag number 13. For that reason, it is good practice to use a defensive programming style and verify that what you’re working with is exactly what you expect. Defensive programming uses assertions to verify that assumptions made are true. For this, you should use the NSAssert method:

-(void) ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event;
{
    CCNode* node = [self getChildByTag:13];

    // defensive programming: verify the returned node is a CCLabelTTF
    NSAssert([node isKindOfClass:[CCLabelTTF class]],images
        @"node is not a CCLabelTTF!");

    CCLabelTTF* label = (CCLabelTTF*)node;
    label.scale = CCRANDOM_0_1();
}

In this case, we expect the node returned by getChildByTag to be an object derived from CCLabelTTF, but we can never be sure, which is why adding an NSAssert to verify the fact is helpful in finding errors before they lead to a crash.

Note that this adds two more lines of code, but in terms of performance things remain the same. The call to NSAssert is completely removed in Release builds, and the cast CCLabelTTF* label = (CCLabelTTF*)node; is what we’ve done already, just on the same line. Essentially, both versions perform exactly the same, but in the second case you get the benefit of being notified when you didn’t get the expected CCLabelTTF object, instead of crashing with an EXC_BAD_ACCESS error.

What Else You Should Know

Since this is the “Getting Started” chapter, I think it’s important to take the opportunity to introduce you to some vital but often overlooked aspects of iOS game development. I want you to be aware of the subtle differences among various iOS devices. In particular, available memory is often incorrectly considered because you can use only a fraction of each device’s memory safely.

I also want you to know that the iOS Simulator is a great tool for testing your game, but it can’t be used to assess performance, memory usage, and other features. The simulator experience can differ greatly from running your game on an actual iOS device. Don’t fall into the trap of making assessments based on your game’s behavior in the iOS Simulator. It’s only the device that counts.

The iOS Devices

When you develop for iOS devices, you need to take into account their differences. Most independent and hobby game developers can’t afford to purchase each slightly different iOS device, of which there are eight at the time of this writing, with roughly two more to be released each year. At the very least, you need to understand that there are important differences.

You might want to refer to Apple’s spec sheets to familiarize yourself with the iOS device technical specifications. The following links list the iPhone, iPod touch, and iPad device specifications, respectively:

Table 2–1 summarizes the most important hardware differences that concern game developers. The table lists iPod touch devices by generation since Apple doesn’t use suffixes like “3G” for its iPod touch models. This table serves to show that the iOS devices are not as homogenous as you might expect. For example, it is noteworthy that the second-generation iPod touch has a faster CPU than the second-generation iPhone 3G, but then the fourth- generation iPod touch has only half the memory of the iPhone 4. And the iPad 2 introduces for the first time a dual-core processor.

images

As you can see, with every new generation, iOS devices usually have a faster CPU, a more powerful graphics chip, and increased memory and screen resolution. This trend will continue, with newer devices getting more and more powerful. If you plan to make money from iOS games, keep in mind that the older models still have significant market share, and this changes only very slowly, much slower than the rate at which new devices are released. Even today, if you do not design your game to run on second-generation devices, you are giving up a significant portion of the market!

Usually when game developers look at hardware features, they tend to focus on the CPU speed and graphics chip to assess what’s technically possible. However, being mobile devices, the iOS devices until the most recent iPhone 4 are limited mostly by the amount of available RAM.

NOTE: RAM is not to be confused with the flash storage memory where MP3s, videos, apps, and photos are stored, of which even the smallest iOS device has 8GB. Flash storage memory is equivalent to the hard drive on your desktop computer. RAM is the memory your application uses to store code, data, and textures while it’s running.

About Memory Usage

Current iOS devices have 128MB, 256MB, or 512MB of RAM installed. However, that’s not the amount of memory available to apps. iOS uses a big chunk of memory all the time, and this is compounded by iOS multitasking introduced with iOS 4. Each device running iOS 4 or newer may be running various background tasks that use up an undefined additional amount of memory.

Over time, iOS developers have been able to close in on the theoretical maximum amount of RAM an app can use before it’s forcibly closed by the OS. Table 2–2 shows what you can expect to work with. Ideally, you want to keep your memory usage below the number in the Memory Warning Threshold column at all times. This is especially challenging on devices with only 128MB of RAM because there is only 20MB to 25MB of RAM more or less guaranteed to be available for an app. Around that point your app might start receiving Memory Warning notifications. You can ignore Memory Warning Level 1 notifications, but if the app continues to use more memory, you may get a Memory Warning Level 2 message, at which point the OS basically threatens to close your app if you don’t free some memory right now. It’s like your mom threatening not to buy your new computer if you don’t clean up your room right now! Please oblige.

images

Cocos2d can help you a little with freeing memory by calling the purge methods. By adding the purgeCachedData method to the AppDelegate’s applicationDidReceiveMemoryWarning method, you can have cocos2d try to free up some more unused memory:

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
    [[CCTextureCache sharedTextureCache] removeUnusedTextures];
    [[CCDirector sharedDirector] purgeCachedData];
}

But when your games get more complex, you may need to implement your own scheme of handling Memory Warning notifications. The problem inherent with Memory Warning notifications is that they can cause performance glitches as your app frees big chunks of memory. If you rely on the cocos2d mechanisms to do this, it may remove a sprite’s texture from memory, but if within a few frames that sprite is needed again, the texture will be reloaded during game play, which is slow. This can create noticeable stutters, so having your own memory management scheme implemented is preferable. You should be able to differentiate between textures that may be needed again soon and those that aren’t needed at all right now. Unfortunately, there’s no one size fits all solution—or I’d provide you with one.

If you are developing on a device with 256MB or 512MB of memory, keep in mind that a great number of iOS devices are models with only 128MB. Unless you plan to limit your game to third-generation and newer iOS devices, you would do well to buy a cheap, used, first- or second-generation device and test your game primarily with that device in order to catch issues of excessive memory consumption early. That’s when they are still easy and cheap to fix, especially if they require a change in the game’s design. In general, it’s advisable to use the device for development that’s the weakest one available to you in terms of hardware capabilities. This helps you catch any performance or low-memory issues as early as possible.

NOTE: It’s also recommended that you test your app on a multitasking-capable device that has a good number of background tasks running in order to test for a worst-case scenario of background tasks using up additional memory. Multitasking is available only for third-generation and newer devices, which means only devices with 256MB of RAM are allowed to run apps in the background. That’s good news, since the 128MB of the first- and second-generation devices is barely enough for a single app. If you design for those devices, and I think you should, you don’t have to worry too much about background tasks eating away your app’s precious memory.

On devices with 128MB of RAM, you can at most allocate around 35MB to 40MB memory. Keep in mind that this is only a theoretical maximum; the number varies on each device and may even depend on which apps the user had previously used. This is the reason app developers recommend rebooting a device if you are experiencing crashes. It can free up some more memory. The number-one cause for apps to quit unexpectedly is that the device ran out of memory. So, be sure to be wary of your app’s memory usage and to frequently reboot your devices when you get weird behavior.

You can measure memory usage using the Instruments application, which is explained in Apple’s Instruments User Guide: http://developer.apple.com/iphone/library/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Introduction/Introduction.html.

The iOS Simulator

Apple’s iOS SDK allows you to run and test iPhone and iPad applications on your Mac with the iOS Simulator. The primary purpose of the iOS Simulator is to let you more rapidly test your application because deployment to an iOS device takes longer and longer as your game gets bigger and bigger. Games in particular use a lot of images and other assets that need to be transferred, slowing down deployment.

However, there are several caveats to using the iOS Simulator. The following sections reveal what the iOS Simulator does not allow you to do. For all these reasons, it is recommended that you test your game early and often on a device. At least after every major change or near the end of the day, you should run a test on your iOS device to verify that the game behaves exactly as intended.

Can’t Assess Performance

The performance of your game running in the iOS Simulator depends entirely on your computer’s CPU. The graphics-rendering process does not even use the hardware acceleration capabilities of your Mac’s graphics chip. That’s why the framerate of your game running in the simulator has no meaning at all. You can’t even be sure that comparing the framerate before and after a change will reveal the same results on the device. In extreme cases, the framerate in the simulator may go up even as it goes down on the device. Always do your performance testing on the device, using the Release build configuration.

Can’t Assess Memory Usage

The iOS Simulator is able to use all the memory available on your computer, so there’s a lot more memory available on the simulator than on the device. This means you won’t get Memory Warning notifications and your game will run fine on the iOS Simulator, but you may be in for a shock (a crash) when you try the game for the first time on an iOS device.

You can, however, assess how much memory is currently used by your game using the iOS Simulator.

Can’t Use All iOS Device Features

Some features, such as device orientation, can be simulated using menu items or keyboard shortcuts, but this comes nowhere close to the experience of a real device. And certain hardware features, such as multitouch input, accelerometer, vibration, or obtaining location information can’t be tested at all on the iOS Simulator because your computer’s hardware can’t simulate these features. No, it doesn’t help to shake your Mac or touch its screen. Try it if you don’t believe me.

TIP: The iSimulate app (www.vimov.com/isimulate) is an invaluable development tool, as it allows an iOS device to send accelerometer, GPS, compass and multi-touch events to an app running in the iOS Simulator.

Runtime Behavior Can Differ

From time to time you may encounter nasty cases where a game runs just fine on the iOS Simulator but crashes on the device or the game slows down for no apparent reason. There may also be graphical glitches that appear only on the iOS Simulator or only on the device. If in doubt and before delving into a prolonged quest to figure out what’s wrong, always try running your game on the device if you’re having trouble on the iOS Simulator, or vice versa. Sometimes, the problem may just go away, but if not, you may get a hint about what’s going on.

About Logging

By default, an Xcode project will have two build configurations named Debug and Release. The main difference between the two is that only in Debug builds are certain functions like CCLOG compiled. This is typically controlled by preprocessor macros like DEBUG and COCOS2D_DEBUG, which control the level of debugging code built into the app. That’s the single most important factor when it comes to performance variations between Debug and Release builds.

NOTE: The CCLOG macro wraps Apple’s NSLog method so that CCLOG is compiled only in Debug builds but omitted in Release builds. I recommend using CCLOG in place of NSLog because logging is for your eyes only. NSLog can slow down your published game because it will be run even in Release builds!

One NSLog or CCLOG in the wrong place can spam the Debugger Console window with logging messages, causing slowdowns and lag. Logging is very slow, and a continuous stream of log messages printed to the Debugger Console can drag your game’s performance down to a crawl. If you suspect your game’s performance to be particularly slow in Debug builds, always check the Debugger Console for excessive logging activities. From the Run menu in Xcode, select Console to show the Debugger Console window.

The exclusion of logging and typically better code optimization settings are the main reason you should only use Release builds to test your game’s performance.

Summary

Wow, that was a lot for a “Getting Started” chapter! In the first part of this chapter you learned to download and set up all the necessary tools to the point where you had your first cocos2d template project running.

I then walked you through the workings of the template project to get you up to speed with how an iOS cocos2d application works in principle and somewhat in detail as well. I do have a pet peeve about proper memory management, which is why I also included those details. I think it’s important because it’s easy to misunderstand or even completely ignore memory management, and then you might be building your game on a very crumbly foundation.

I did manage to sneak in a short “do it yourself” section to at least show you how touch input is done in cocos2d and how cocos2d objects are stored and retrieved.

Finally, I thought it important to give you the details about the various iOS devices and what you can expect in terms of available memory. I also discussed the simulator and how it differs for testing your game compared with testing it on a device.

In the next chapter, you’ll learn all the essential features of cocos2d, which will bring you closer to making a complete cocos2d game.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.69.199