We'll start building the project from the default cocos2d v2.x - cocos2d iOS template. Once the project is created, we first remove the HelloWorldLayer.h/.m
files. HelloWorld
is a good starting point for learning the code structure, but we don't really want (or need) this boilerplate class for anything
(don't forget to remove the #import "HelloWorldLayer.h"
at the top of the
IntroLayer.m
class). For now we'll leave the reference in the bottom of the IntroLayer.m
's makeTransition
class.
One of the most commonly used classes in the cocos2d framework is probably the CCLayer
. A CCLayer
is the object that is (usually) represented on the screen, and acts as our "canvas" for our game. We use the
CCLayer
object as a basis, and then create subclasses of it to add our own game code.
There is another often-used class, the CCScene
class. A
CCScene
class can be thought of as a "container" for CCLayer
objects. A
CCScene
object is rarely used for much more than adding CCLayer
s as children. A good comparison is like the creation of cartoons before the age of computers. Each scene was assembled from a stack of transparent plastic sheets, each with a different part of the scene on it: each main character would have their own layer, another for the background, another for each different element of the scene. Those plastic sheets are the equivalent of
a CCLayer
objects, and the CCScene
class is where these are stacked up to display on screen.
We will start with a basic
CCLayer
subclass, MTMenuLayer
. We create a title, and a basic menu. We need to pay attention to how we call the MTPlayfieldScene
class (our main game screen) from the menu.
Filename: MTMenuLayer.m
-(void) startGameEasy { [[CCDirector sharedDirector] replaceScene: [MTPlayfieldScene sceneWithRows:2 andColumns:2]]; } -(void) startGameMedium { [[CCDirector sharedDirector] replaceScene: [MTPlayfieldScene sceneWithRows:3 andColumns:4]]; } -(void) startGameHard { [[CCDirector sharedDirector] replaceScene: [MTPlayfieldScene sceneWithRows:4 andColumns:5]]; }
You will notice that the
startGameXXX
methods are calling a custom constructor for the scene, rather than the normal [MyLayer scene]
that is commonly used. We will explain the
sceneWithRows:andColumns:
method shortly.
This book will not include the complete code within the text. Portions that aren't interesting for the discussion will be omitted.
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files
e-mailed directly to you.
Oh, you noticed? The boilerplate cocos2d template includes a class method +(id)
scene inside the layer (in HelloWorldLayer
). While this approach works, it can lead to confusion as we build more complex scenes with multiple layers. Using the template-based approach may seem odd when you call a method that
takes a CCScene
object as a parameter, yet you pass it a value like [MySpecialLayer scene]
. So are you referencing a CCScene
or CCLayer
object? It makes a lot more logical sense to us that you would, in this example, pass a value like [MySpecialScene scene]
. It is less confusing to pass a scene object when a CCScene
is requested. A CCScene
object is a higher-level container that was designed to contain CCLayer
objects, so why not keep it as its own class? Let's go ahead and examine our approach:
Filename: MTMenuScene.h
#import <Foundation/Foundation.h> #import "cocos2d.h" #import "MTMenuLayer.h" @interface MTMenuScene : CCScene { } +(id)scene; @end
Filename: MTMenuScene.m
#import "MTMenuScene.h" @implementation MTMenuScene +(id)scene { return( [ [ [ self alloc ] init ] autorelease ] ); } -(id) init { if( (self=[super init])) { MTMenuLayer *layer = [MTMenuLayer node]; [self addChild: layer]; } return self; } @end
Here we have followed the convention that the scene method returns an
autoreleased
object. We do not explicitly call alloc
on it (when we instantiate the class), so we don't "own" the object.
Now we can go back to the IntroLayer.m
file, and change the
makeTransition
method to point to our new menu scene:
-(void) makeTransition:(ccTime)dt { [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[MTMenuScene scene] withColor:ccWHITE]]; }
We also need to make sure we are importing the
MTMenuScene.h
file in the AppDelegate.m
file. Now that our menu is complete, we can concentrate on the game itself.
Note
It is important to note that this design of using CCScene
as a separate class in the structure is not universally adopted. Many people choose to follow the same approach as the templates. Both ways will work, but we are of the "camp" that strongly believes these should kept separate, as we have done here. Both ways are perfectly valid coding practice, and you are free to structure your code in other way.
Next, we will add a CCScene
class to drive our main game screen, here named MTPlayfieldScene
. Much of this looks the same as the
MTMenuScene
class we defined earlier, except here we define a method
sceneWithRows:andColumns:
instead of the simpler scene method we used in the previous code.
Filename: MTPlayfieldScene.m
+(id) sceneWithRows:(NSInteger)numRows andColumns:(NSInteger)numCols { return [[[self alloc] sceneWithRows:numRows andColumns:numCols] autorelease]; } -(id) sceneWithRows:(NSInteger)numRows andColumns:(NSInteger)numCols { if( (self=[super init])) { // Create an instance of the MTPlayfieldLayer MTPlayfieldLayer *layer = [MTPlayfieldLayer layerWithRows:numRows andColumns:numCols]; [self addChild: layer]; } return self; }
Here we have the custom sceneWithRows:andColumns:
method we referenced in the MTMenuLayer
earlier. The class method handles the alloc
and init
methods, and identifies it as an autoreleased object, so we don't have to worry about releasing it later. The sceneWithRows:andColumns:
method passes the rows and columns variables directly to the MTPlayfieldLayer
class' custom init
method, layerWithRows:andColumns:
. This lets us pass the requested values through the scene to the layer, where we can use the values later.