Jan 29

If you look at the source code for Cocos2D, you will find this little note in Director.m:

	// XXX it's using hardcoded values.
	// What if the the screen size changes in the future?

This isn’t a problem if you want to run your iPhone app in what I call “boxed” mode – in it’s native resolution on the center of the iPad screen. But if you want to start working on your code in the native iPad resolution of 1024 x 768 you will need to get the latest build (SVN r1753).

See this thread: http://www.cocos2d-iphone.org/forum/topic/4108

Screenshot

Note that the controls are now properly scaled. Obviously one pending issue is substituting a background graphic with the proper resolution.

iPad app using hacked Cocos2D engine

Tags: ,

Oct 30

I’ve added code to my Cocos2D test harness to show you how to handle coordinating touch events among multiple sprites. The class I created to manage the sprites was based on code that I found in this article:

http://juanmunozar.blogspot.com/2009/02/cocos2d-iphone-dynamically-touch.html

If you have git installed you can retrieve the latest copy of the project via the instructions at the end of this article.

Getting Started

In order to complete this tutorial you first you need to go through the steps of my posts:

If you are using git you may want to save and tag the original project and try expanding on it here in a new branch.

Graphic Files

For this tutorial you will need to copy some additional graphic files from the cocos2d-iphone sample folders to your projects Resources folder:

  • Resources/Images/blocks.png

Classes / TestScene.m

First, you need to add a string identifying the new test layer to the transitions array in TestScene.m:

static NSString *transitions[] = {
	@"TestLayer01",
	@"TestLayer02",
	@"TestLayer03",
};

Classes / mcaTouchSprite.h

Here I introduce a new class based on code in the referenced article. As you can see I’ve trimmed it down quite a lot to demo just the basics.

Something that you may not have seen before is the “SEL” property type. Think of it as a function pointer. It lets you dynamically assign a function to call when a sprite is touched. You will see how this is done later in the code.

//
//  mcaTouchSprite.h
//
//  Created by Mitchell Allen on 10/6/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// References:
// http://juanmunozar.blogspot.com/2009/02/cocos2d-iphone-dynamically-touch.html
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/424
//
 
#import <UIKIt/UIKit.h>
#import "cocos2d.h"
#import "chipmunk.h"
 
 
@interface mcaTouchSprite : Sprite {
 
	@private
 
	SEL		__onTouchBegan;
}
 
#pragma mark properties
 
@property (nonatomic, assign) SEL onTouchBegan;
 
#pragma mark class methods
 
+(NSMutableArray *)allMySprites;
+(void) track: (mcaTouchSprite *)aSprite;
+(void) untrack: (mcaTouchSprite *)aSprite;
 
#pragma mark instance methods
 
- (CGRect) rect;
 
@end

Classes / mcaTouchSprite.m

//
//  mcaTouchSprite.m
//
//  Created by Mitchell Allen on 10/6/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/424
//
 
#import "mcaTouchSprite.h"
 
@implementation mcaTouchSprite
 
@synthesize onTouchBegan = __onTouchBegan;
 
static NSMutableArray *allMySprites = nil;
 
+(NSMutableArray *)allMySprites {
    @synchronized(allMySprites) {
        if (allMySprites == nil)
            allMySprites = [[NSMutableArray alloc] init];
        return allMySprites;
    }
	return nil;
}
 
- (CGRect) rect {
	float w = self.contentSize.width;
	float h = self.contentSize.height;
	float x = self.position.x - w/2;
	float y = self.position.y - h/2;
	return CGRectMake(x,y,w,h);
}
 
+(void)track: (mcaTouchSprite *)aSprite {
    @synchronized(allMySprites) {
		NSUInteger i, count = [allMySprites count];
		for(i = 0; i < count ; i++){
			mcaTouchSprite * obj 
				= (mcaTouchSprite *)[allMySprites objectAtIndex:i];
			if(obj == aSprite){
				return;
			}
		}
 
        [[mcaTouchSprite allMySprites] addObject:aSprite];
    }
}
 
+(void)untrack: (mcaTouchSprite *)aSprite {
    @synchronized(allMySprites) {
        [[mcaTouchSprite allMySprites] removeObject:aSprite];
    }
}
 
-(void)dealloc {
    [mcaTouchSprite untrack:self];
    [super dealloc];
}
 
@end

Classes / mcaBoxSprite.h

This is my first pass at creating a sprite that has some built in functionality. In this case a turn method that lets you rotate the sprite on the screen through code. To demo that touch events are actually being processed, the sprite will turn n degrees every time it is touched. The method does that by adding the amount of degrees desired to self.rotation which it inherits from the base class.

//
//  mcaCarSprite.h
//  Potholes_001_001
//
//  Created by Mitchell Allen on 10/7/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/424
//
 
#import "mcaTouchSprite.h"
 
@interface mcaBoxSprite : mcaTouchSprite { }
 
-(void) turn:(float) n;
 
@end

Classes / mcaBoxSprite.m

//
//  mcaBoxSprite.m
//
//  Created by Mitchell Allen on 10/7/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/424
//
 
#import "mcaBoxSprite.h"
 
#import "math.h"
 
@implementation mcaBoxSprite
 
-(void) turn:(float) n { 
 
	self.rotation += n;
}
 
@end

Classes / MyTests.h

Now you need to include references to the new header files and add the interface declaration for our new test layer to MyTests.h:

//
//  MyTests.h
//  TestGame
//
//  Created by Mitchell Allen on 9/20/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
 
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/371
// http://mitchallen.com/iphone/archives/304
// http://mitchallen.com/iphone/archives/424
 
#import "TestScene.h"
 
#import "mcaTouchSprite.h"
#import "mcaBoxSprite.h"
 
///////////////////////////////////////////////////////
//
// Adding Test Layers to Test Game
//
// When a  dervied TestLayer is added, you must add it's name to 
// the NSString transitions array in TestScene.m for it to appear 
// in the test game.
 
#pragma mark -
 
@interface TestLayer01 : TestLayer
{}
@end
 
#pragma mark -
 
@interface TestLayer02 : TestLayer
{}
@end
 
#pragma mark -
 
#define LAYER_03_BOXES	4
 
@interface TestLayer03 : TestLayer
{
	mcaBoxSprite *box[ LAYER_03_BOXES ];
}
@end

Classes / MyTests.m

Finally, append the code for TestLayer03 to MyTests.m.

Where as the previous layers did their setup in the onEnter method, here we do setup in the init method.

You will notice that we loop through a collection of box sprites, assigning the function pointer (onTouchBegan) to the method touchBox and flag the box sprite for touch tracking. We also need to make sure isTouchEnabled is set to YES. When touchBox is called, the turn method on the touched box is called – rotating the sprite.

Most of the main processing happens in the ccTouchesBegan method. This method attempts to figure out which if any box sprite was called, then calls whatever method onTouchBegan is pointing to, passing in the object.

#pragma mark -
 
@implementation TestLayer03
 
-(id) init {
 
	self = [super init];
 
	if(self != nil) {
 
		//////////////////////////////
		// Add Boxes
 
		for( int i = 0; i < LAYER_03_BOXES; i++ ) {
 
			// Copy blocks.png from Cocos2D resources.
 
			box[i] = 
			  [[mcaBoxSprite spriteWithFile:@"blocks.png"] retain];
 
			box[i].onTouchBegan = @selector(touchBox:);
 
			[mcaBoxSprite track:box[i]];
 
			float bx = 50;
			float by = 50 * (1 + i);
 
			box[i].position = cpv( bx, by );
 
			box[i].scaleY = 0.5f;
			box[i].scaleX = 0.5f;
 
			box[i].rotation = 0.0f;
 
			[self addChild: box[i]];
		}
 
		//////////////////////////
		// Enable interaction.
 
		isTouchEnabled = YES;	// To enable touching
	}
 
	return self;
}
 
-(void) onEnter
{	
	[super onEnter];
}
 
-(void) onExit
{		
	[super onExit];
 
	for( int i = 0; i < LAYER_03_BOXES; i++ ) {
 
		[mcaBoxSprite untrack:box[i]];
	}
 
}
 
-(void) touchBox: (id) theBox {
 
	[theBox turn: 10];
 
}
 
- (BOOL)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 
    UITouch *touch = [touches anyObject];
 
	CGPoint location = [[Director sharedDirector] 
						convertCoordinate: 
						[touch locationInView:touch.view]];
 
    NSArray * mySprites = [mcaTouchSprite allMySprites];
    NSUInteger i, count = [mySprites count];
 
	// NSLog( @"COUNT = %d", count );
 
    for (i = 0; i < count; i++) {
 
        mcaTouchSprite * obj 
			= (mcaTouchSprite *)[mySprites objectAtIndex:i];
 
        if (CGRectContainsPoint([obj rect], location) ) {
 
            // code here is only executed if obj has been touched
			// NSLog( @"Sprite touched." );
 
			if( [self respondsToSelector: obj.onTouchBegan ] ) {
 
				[self 
					performSelector: obj.onTouchBegan 
					withObject: obj];
			}
 
			return kEventHandled;
        }
    }
 
	return false;
}
 
- (BOOL)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
 
	return false;
}
 
-(void) dealloc
{
	for( int i = 0; i < LAYER_03_BOXES; i++ ) {
 
		[box[i] release];
	}
 
	[super dealloc];
}
 
-(NSString *) title
{
	// Override the base classes title 
	// that appears on the screen.
 
	return @"Test Layer 03 - Touch";
}
@end

References

http://juanmunozar.blogspot.com/2009/02/cocos2d-iphone-dynamically-touch.html

Git Clone

If you have git installed, you can clone a copy of this project with the following command:

git clone git://github.com/mitchallen/TestGame.git

I may add more code to the repository. So to get back to the original state of this tutorial, you will need to execute the following at the command line:

git checkout v300

See previous articles for previous states.

Tags:

Sep 20

In this tutorial I’m going to expand on my previous example TestGame featured in my post Getting Started with the Cocos2d Game Engine.  This follow up borrows heavily from the cocos2d sample file SpritesTest.  If you haven’t seen the SpritesTest demo, see my article on Running the Cocos2d examples. The objective is to reverse engineer that demo and use it to show you how to build your own test app.

Why Build a Test App ?

My background is in testing software libraries.  I have a lot of experience breaking down functionality into manageable test harnesses.  The test harnesses usually let you select one isolated feature and test it.  It’s a great way for learning how a library works.  It’s also a great way to try something.  By building a test harness you don’t have to build a whole project from scratch just to test one feature.  This is especially handy when you run into features that you can only test on the actual device.

Getting Started

In order to complete this tutorial you first you need to go through the steps of my post Getting Started with the Cocos2d Game Engine.

If you are using git you may want to save and tag the original project and try expanding on it here in a new branch.

Graphic Files

For this tutorial you will need to copy some additional graphic files from the cocos2d-iphone sample folders to your projects Resources folder:

  • Resources/Images/grossini images/grossini.png (male cartoon character)
  • Resources/Images/grossini images/grossini_sister1.png (female cartoon character)
  • Resources/Images/menu related images/b1.png (back arrow)
  • Resources/Images/menu related images/b2.png (back arrow highlighted)
  • Resources/Images/menu related images/f1.png (next arrow)
  • Resources/Images/menu related images/f2.png (next arrow highlighted)
  • Resources/Images/menu related images/r1.png (reset button)
  • Resources/Images/menu related images/r2.png (reset button highlighted)

Classes / TestGameAppDelegate.m

For this tutorial I’ve borrowed from the SpritesTest code and added some new functionality to TestGameAppDelegate.m:

//
//  TestGameAppDelegate.m
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright __MyCompanyName__ 2009. All rights reserved.
//
// Some code and images borrowed from cocos2d SpritesTest sample code.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/371
// http://mitchallen.com/iphone/archives/304
 
#import "TestGameAppDelegate.h"
 
@implementation TestGameAppDelegate
 
- (void)applicationDidFinishLaunching:(UIApplication *)application { 
 
	UIWindow *window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
 
	[window setUserInteractionEnabled:YES];
	[window setMultipleTouchEnabled:YES];
 
	// DEPRECATED: [[Director sharedDirector] setLandscape: YES];
	[[Director sharedDirector] setDeviceOrientation: CCDeviceOrientationLandscapeLeft ];
	[[Director sharedDirector] attachInWindow: window];
 
	// display FPS (useful when debugging)
	[[Director sharedDirector] setDisplayFPS:YES];
 
	// frames per second
	[[Director sharedDirector] setAnimationInterval:1.0/60];
 
	[window makeKeyAndVisible];
 
	// Default texture format for PNG/BMP/TIFF/JPEG/GIF images
	// It can be RGBA8888, RGBA4444, RGB5_A1, RGB565
	// You can change anytime.
	[Texture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
 
	TestScene *game = [TestScene node];
	[[Director sharedDirector] runWithScene: game];
}
 
// getting a call, pause the game
-(void) applicationWillResignActive:(UIApplication *)application
{
	[[Director sharedDirector] pause];
}
 
// call got rejected
-(void) applicationDidBecomeActive:(UIApplication *)application
{
	[[Director sharedDirector] resume];
}
 
// purge memroy
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
	[[TextureMgr sharedTextureMgr] removeAllTextures];
}
 
- (void) dealloc
{
		[super dealloc];
}
 
// next delta time will be zero
-(void) applicationSignificantTimeChange:(UIApplication *)application
{
	[[Director sharedDirector] setNextDeltaTimeZero:YES];
}
 
@end

Test Scene

There are no changes to TestScene from the previous project.  As a reminder:

  • A simple TestScene class is defined based on cocos2d Scene class
  • The TestScene loads a TestLayer based on a cocos2d Layer class

Test Layer

There have been several changes made to TestLayer.

  • Now TestLayer serves as a base class for additional test layers that you can customize based on what you want to test
  • It manages the loading of two sprites from the cocos2d samples: grossini and tamara
  • It loads a graphical menu for navigating between layers using arrows and restarting a layer
  • It has several functions to work with the menu to switch between layers
  • It has a centerSprites method for centering the two test sprites
  • It has a title method used by the UI that can be overridden by derived classes

Classes/TestScene.h

Both TestScene and TestLayer are defined in Classes/TestScene.*

//
//  TestScene.h
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/371
// http://mitchallen.com/iphone/archives/304
 
#import
#import "cocos2d.h"
#import "chipmunk.h"
 
#pragma mark -
 
@interface TestScene : Scene { }
@end
 
#pragma mark -
 
@interface TestLayer : Layer {
	Sprite * grossini;
	Sprite *tamara;
}
-(void) centerSprites;
-(NSString*) title;
 
-(void) setLayer: (Class) classLayer;
 
-(void) backCallback:(id) sender;
-(void) nextCallback:(id) sender;
-(void) restartCallback:(id) sender;
@end

Classes/TestScene.m

One important thing to note here is the global static transitions string array.  When you add a new class based on TestLayer (see MyTests.* below), you need to add a string containing the name to the array. The navigation code uses NSClassFromString to convert the currently indexed string to a layer object to load.

//
//  TestScene.m
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/371
// http://mitchallen.com/iphone/archives/304
//
 
#import "TestScene.h"
 
#pragma mark -
 
@implementation TestScene
 
-(id) init {
	self = [super init];
	if( self != nil ) {
		[self addChild: [TestLayer node] z:1];
	}
	return self;
}
 
@end
 
#pragma mark -
 
static int sceneIdx=-1;
 
///////////////////////////////////////////////////
// In order for a test layer to appear you must
// add a string for its class name to this array.
// Test layers are defined in MyTests.*
 
static NSString *transitions[] = {
	@"TestLayer01",
	@"TestLayer02" };
 
Class nextAction()
{
	// Retrieve the name of the next layer class
	// and return a class based on that string.
 
	sceneIdx++;
	sceneIdx = sceneIdx % ( sizeof(transitions) / sizeof(transitions[0]) );
	NSString *r = transitions[sceneIdx];
	Class c = NSClassFromString(r);
	return c;
}
 
Class backAction()
{
	// Retrieve the name of the previous layer class
	// and return a class based on that string.
 
	sceneIdx--;
	if( sceneIdx < 0 )
		sceneIdx = sizeof(transitions) / sizeof(transitions[0]) -1;
	NSString *r = transitions[sceneIdx];
	Class c = NSClassFromString(r);
	return c;
}
 
Class restartAction()
{
	// Retrieve the name of the current layer class
	// and return a class based on that string.
 
	if( sceneIdx < 0 || sceneIdx >= sizeof(transitions) ) return nil;
 
	NSString *r = transitions[sceneIdx];
	Class c = NSClassFromString(r);
	return c;
}
 
#pragma mark -
 
@implementation TestLayer
 
-(id) init {
	self = [super init];
	if(self != nil) {		
 
		// Initialize and add test images so they can be used by derived test layers.
 
		// Note that because we 'retain' the images,
		// we will need to release them in dealloc.
 
		grossini = [[Sprite spriteWithFile:@"grossini.png"] retain];
		tamara = [[Sprite spriteWithFile:@"grossinis_sister1.png"] retain];
 
		[self addChild: grossini z:1];
		[self addChild: tamara z:2];
 
		[self centerSprites];
 
		CGSize s = [[Director sharedDirector] winSize];
 
		// Display the title property for the current layer.
 
		Label* label = [Label labelWithString:[self title] fontName:@"Arial" fontSize:32];
		[self addChild: label];
		[label setPosition: ccp(s.width/2, s.height-50)];
 
		// Create a graphics-based layer navigation menu to be used by all derived test layers.
 
		MenuItemImage *item1 = [MenuItemImage itemFromNormalImage:@"b1.png" selectedImage:@"b2.png" target:self selector:@selector(backCallback:)];
		MenuItemImage *item2 = [MenuItemImage itemFromNormalImage:@"r1.png" selectedImage:@"r2.png" target:self selector:@selector(restartCallback:)];
		MenuItemImage *item3 = [MenuItemImage itemFromNormalImage:@"f1.png" selectedImage:@"f2.png" target:self selector:@selector(nextCallback:)];
 
		Menu *menu = [Menu menuWithItems:item1, item2, item3, nil];
		menu.position = CGPointZero;
		item1.position = ccp(480/2-100,30);
		item2.position = ccp(480/2, 30);
		item3.position = ccp(480/2+100,30);
		[self addChild: menu z:1];
	}
	return self;
}
 
-(void) centerSprites
{
	// Take our two test sprites and center them on the screen.
 
	CGSize s = [[Director sharedDirector] winSize];
 
	[grossini setPosition: ccp(s.width/3, s.height/2)];
	[tamara setPosition: ccp(2*s.width/3, s.height/2)];
}
 
-(NSString*) title
{
	// Create a default title for the opening layer.
 
	// Test layers should override this call.
 
	return @"Press Arrows to Continue";
}
 
-(void) dealloc
{
	// Since our test sprites were retained,
	// we must now release them.
 
	[grossini release];
	[tamara release];
	[super dealloc];
}
 
-(void) setLayer: (Class) classLayer {
 
	// Replace the current layer with one based another class object.
 
	if( classLayer == nil ) return;
 
	Scene *s = [Scene node];
	[s addChild: [classLayer node]];
	[[Director sharedDirector] replaceScene: s];
}
 
-(void) restartCallback: (id) sender
{
	// When the user clicks the restart button
	// (the green circle in the current UI)
	// reload the current layer,
	// triggering a restart of the action
 
	[self setLayer: restartAction()];
}
 
-(void) nextCallback: (id) sender
{
	// When the user clicks the next arrow
	// (the green right arrow in the current UI)
	// load the next layer in the list.
 
	[self setLayer: nextAction()];
}
 
-(void) backCallback: (id) sender
{
	// When the user clicks the back arrow
	// (the green left arrow in the current UI)
	// load the previous layer in the list.
 
	[self setLayer: backAction()];
}
 
@end

Classes/MyTests.h

When adding a new derived TestLayer you generally don’t have to do a lot in the header file (unless you want to add more functions and things like retained sprites to track).  For simple layers you just need to define interfaces based on TestLayer.

//
//  MyTests.h
//  TestGame
//
//  Created by Mitchell Allen on 9/20/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
 
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/371
// http://mitchallen.com/iphone/archives/304
 
#import "TestScene.h"
 
///////////////////////////////////////////////////////
//
// Adding Test Layers to Test Game
//
// When a  dervied TestLayer is added, you must add it's name to
// the NSString transitions array in TestScene.m for it to appear
// in the test game.
 
@interface TestLayer01 : TestLayer
{}
@end
 
@interface TestLayer02 : TestLayer
{}
@end

Classes/MyTests.m

For simple layers you just need to define two things:

  • An onEnter method for setting up the actions in your layer
  • A title method that overrides the base TestLayer title method and returns a pointer to an NSString used by the UI

You also need to remember this important step:

  • When you add a new layer you need to add the name as a string to the transistions array in TestScene.m

For this tutorial I just borrowed the SpritesTest demo code from the SpriteMove and SpriteRotate classes.

You can use this file to add as many test layers as you would like.

//
//  MyTests.m
//  TestGame
//
//  Created by Mitchell Allen on 9/20/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
// See these links for additional info and credit:
// http://mitchallen.com/iphone/archives/371
// http://mitchallen.com/iphone/archives/304
 
#import "MyTests.h"
 
#pragma mark -
 
@implementation TestLayer01
 
-(void) onEnter
{
	// Code borrowed from cocos2d sample:
	// SpritesTest.m - SpriteMove class
 
	[super onEnter];
 
	CGSize s = [[Director sharedDirector] winSize];
 
	id actionTo = [MoveTo actionWithDuration: 2 position:ccp(s.width-40, s.height-40)];
 
	id actionBy = [MoveBy actionWithDuration:2  position: ccp(80,80)];
	id actionByBack = [actionBy reverse];
 
	[tamara runAction: actionTo];
	[grossini runAction: [Sequence actions:actionBy, actionByBack, nil]];
 
}
 
-(NSString *) title
{
	// Override the base classes title
	// that appears on the screen.
 
	return @"Test Layer 01 - Move";
}
@end
 
#pragma mark -
 
@implementation TestLayer02
 
-(void) onEnter
{
	// Code borrowed from cocos2d sample:
	// SpritesTest.m - SpriteRotate class
 
	[super onEnter];
 
	[self centerSprites];
 
	id actionTo = [RotateTo actionWithDuration: 2 angle:45];
	id actionBy = [RotateBy actionWithDuration:2  angle: 360];
	id actionByBack = [actionBy reverse];
 
	[tamara runAction: actionTo];
	[grossini runAction: [Sequence actions:actionBy, actionByBack, nil]];
}
 
-(NSString *) title
{
	// Override the base classes title
	// that appears on the screen.
 
	return @"Test Layer 02 - Rotate";
}
@end

Compile and Run

  • Click: Build and Go

Git Clone

If you have git installed, you can clone a copy of this project with the following command:

git clone git://github.com/mitchallen/TestGame.git

For my next tutorial I will be adding code to the repository. So to get back to the original state of this tutorial, you will need to execute the following at the command line:

git checkout v200

To get back to the state used in the previous tutorial you can use:

git checkout v100

Both v100 and v200 are tags that I’ve set on the master branch.

Tags:

Sep 16

Introduction

What follows are notes that I gathered from looking at various tutorials on getting started with the Cocos2D Game Engine. You can find the tutorials that I used as a source for my notes here:

Additional links may be found in my entry here:

This tutorial was tested with cocos2d-iphone-0.8 – I had trouble compiling with 0.8.1.

UPDATE: I’ve added some notes for compiling using 0.8.2 – with the help of some reader comments below.

Build Options

There are a number of options for creating a Cocos2D Game Project:

  1. Use the engine as a shared library
  2. Start from a template
  3. Copy the engine components and put them in your project

For simplicity this article covers Option #3 – copying the engine components to your project.

If you would like to know more about the other options, see these links:

Setting Up the Project

  • Download and unpack the latest stable release of the Cocos2D Game Engine from here: http://code.google.com/p/cocos2d-iphone/
  • Launch Xcode
  • Select: File / New Project … / [iPhone OS : Application] / Window-based Application / Choose …
  • Save As: TestGame, Save
  • Delete: Resources / MainWindow.xib
  • Edit: TestGame-Info.plist:
  • Delete the line: Main nib file base name : MainWindow
  • Add the line: Status bar is initially hidden – and check it
  • Save the file
  • Click on the TestGame folder icon at the top of the Groups & Files pane
  • Select: Project / Add to Project … / cocos2d-phone-x.y.z / cocos2d
  • Hold down the Apple-key and also select: external
  • Click: Add
  • Check: Copy items in destination group’s folder (if needed)
  • Click: Recursively create groups for any added folders
  • Click: Add
  • Note that if you mess up and need to delete the folders, you also may have to go to the project folder itself and delete them.  Otherwise you may get an error if trying to add them again.
  • In the external folder, delete everything that is NOT the Chipmunk folder (if you are using 0.8.2 keep the FontLabel folder too!).  Why?  Because some of those items are experimental and may not compile.
  • From within the external / Chipmunk folder, delete everything that is NOT the src folder.
  • Right-click on the Resources folder and select: Add / Existing Files …
  • If using 0.8: cocos2d / Resources / Images / fps_images.png
  • If using 0.8.2: cocos2d / Resources / Fonts / fps_images.png
  • Add / Check Copy …, Recursive …, Add
  • Right-click on the Frameworks folder and select: Add / Existing Frameworks …
  • Select the Frameworks folder
  • Select: OpenGLES.framework
  • Click Shift and also select: QuartzCore.framework and click: Add, click Add again

Additional Steps for 0.8.2

If you are using 0.8.2 (or possibly a later version) you will need to also do the following steps to get the project to compile:

  • Double-click on Targets \ TestGame
  • Make sure that the General tab is selected in the dialog box
  • There are two sets of + / - buttons in the dialog, click the + button on the bottom under the list of Linked Libraries
  • Select libz.1.2.3.dylib and Add it

Add New Files

  • Right-click on the Classes folder and select:
  • Add / New File … / [iPhone OS : Cocoa Touch Class ] / Object-C class / [NSObject] / Next
  • File Name: TestScene.m, leave Also create “TestScene.h checked and click Finish

Make the following changes to Classes / TestScene.h:

  • Remove the line:  #import <Foundation/Foundation.h>
  • Add the following import lines:
  • #import <UIKit/UIKit.h>
  • #import “cocos2d.h”
  • #import “chipmunk.h”
  • Change the class definition so that it is based on Scene instead of NSObject
  • At the bottom of the file add another @interface called TestLayer based on Layer
//
//  TestScene.h
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
 
#import
#import "cocos2d.h"
#import "chipmunk.h"
 
@interface TestScene : Scene { }
@end
 
@interface TestLayer : Layer { }
@end

Make the following changes to Classes / TestScene.m:

  • Add init method that adds a TestLayer to the scene
  • Add an @implementation for TestLayer
  • Add init method to TestLayer that puts something on the screen
//
//  TestScene.m
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//
 
#import "TestScene.h"
 
#pragma mark -
 
@implementation TestScene
 
-(id) init {
	self = [super init];
	if( self != nil ) {
		[self addChild: [TestLayer node] z:1];
	}
	return self;
}
 
@end
 
#pragma mark -
 
@implementation TestLayer
 
-(id) init {
	self = [super init];
	if(self != nil) {
		// Note that if you spell the fontName wrong, the label won't appear.
		Label *test = [Label labelWithString:@"Hello World" fontName: @"Helvetica" fontSize: 24];
		test.position = cpv(160, 240);	// cpv = Chipmunk vector
		[self addChild: test];	// Add the label to the layer.
	}
	return self;
}
 
@end

Edit the Existing Files

Make the following changes to Classes / TestGameAppDelegate.h:

  • Add two #import statements:
  • #import “cocos2d.h”
  • #import “TestScene.h”
  • Remove the two lines referencing the window property
//
//  TestGameAppDelegate.h
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright __MyCompanyName__ 2009. All rights reserved.
//
 
#import 
 
#import "cocos2d.h"
#import "TestScene.h"
 
@interface TestGameAppDelegate : NSObject  {
}
 
@end

Make the following changes to Classes / TestGameAppDelegate.m:

  • Remove the line: @synthesize window;
  • Remove the dealloc method
  • Clear the contents of the method: applicationDidFinishLaunching and replace it with the code below
  • If you don’t want your game to start in landscape mode, comment out the line containing setDeviceOrientation
//
//  TestGameAppDelegate.m
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright __MyCompanyName__ 2009. All rights reserved.
//
 
#import "TestGameAppDelegate.h"
 
@implementation TestGameAppDelegate
 
- (void)applicationDidFinishLaunching:(UIApplication *)application { 
 
	UIWindow *window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]];
 
	[window setUserInteractionEnabled:YES];
	[window setMultipleTouchEnabled:YES];
 
	// DEPRECATED: [[Director sharedDirector] setLandscape: YES];
	[[Director sharedDirector] setDeviceOrientation: CCDeviceOrientationLandscapeLeft ];
	[[Director sharedDirector] attachInWindow: window];
	[window makeKeyAndVisible];
 
	TestScene *game = [TestScene node];
	[[Director sharedDirector] runWithScene: game];
}
 
@end

Make the following changes to Other Sources / main.m:

  • Change the last argument in the call to UIApplicationMain to a string containing the name of the delegate: @”TestGameAppDelegate”
//
//  main.m
//  TestGame
//
//  Created by Mitchell Allen on 9/16/09.
//  Copyright __MyCompanyName__ 2009. All rights reserved.
//
 
#import 
 
int main(int argc, char *argv[]) {
 
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
	// MCA - changed last argument to name of delegate.
    int retVal = UIApplicationMain(argc, argv, nil, @"TestGameAppDelegate");
 
    [pool release];
    return retVal;
}

Compile and Run

  • Click: Build and Go

Git Clone

If you have git installed, you can clone a copy of this project with the following command:

git clone git://github.com/mitchallen/TestGame.git

For my next tutorial I will be adding code to the repository. So to get back to the original state of this tutorial, you will need to execute the following at the command line:

git checkout v100

Tags: , ,

Aug 14

I paid for and downloaded a PDF copy of iPhone Cool Projects from Apress. You can find a copy of it here: http://www.apress.com/book/view/143022357x.  Or if you prefer the old fashioned way, see my book store links if you want to order a copy from Amazon.

I haven’t had time to read much of the book. So instead of a book review, this is more of an FYI.

I was interested in the book for the following reasons:

  • Apress did a great job with Beginning iPhone Development and iPhone Games Projects
  • There’s a chapter that covers cocos2d and the Chipmunk physics engine
  • Neil Mix – who wrote Pandora Radio for the iPhone – has a chapter on audio streaming
  • There’s a chapter on multi-threading
  • There’s another chapter on writing a game

As always, Apress goes for quality over quantity.  If you like your books by the pound, you may be disappointed to find that the book only has seven chapters.  But it looks to me like there is a wealth of information in it.

Tags: , ,

Jul 24

Even though I’m trying to master 3D game development, I’m also interested in developing 2D games.  I’ve been looking at various iPhone game engines and am currently exploring Cocos2D.  The thing I like most about it is the price – you can download it for free.  See the project page for licensing info.

I’m still exploring the SDK, so don’t have a lot of code to show myself.  So to get you started, here are a few links:

Tags: