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: ,

Jan 29

Tags: ,

Jan 28

Below are some screenshots of the new iPad simulator. To use it you will need to install the latest Beta SDK that was announced yesterday, along with the announcement of the new iPad.

To get your app to work in the iPad simulator you will need to do a couple of things:

  1. Switch your build to: Simulator 3.2 | Debug
  2. When your app is running in the simulator, select: Hardware / Device / iPad

Universal Applications

The above was just to get you started and see your app running on the iPad in what I call a “boxed” version. Ideally you would want to work towards what Apple refers to as a “universal application” – that is optimized for whichever device it happens to be running on. Though Apple notes that universal binaries won’t be available in the initial seed release of 3.2. See the Beta doc for more info.

Creating a true iPad target

To turn an existing app into a native iPad app, right-click on your target and select: Transition. Then change your target executable to the new iPad version. Note that right-clicking on the iPad target and selecting Build “…-iPad” Start doesn’t work if the wrong target is selected in the IDE dropdown. Your app will still appear as a boxed iPhone app. Once you have it running as a true iPad app, if you hard-coded a few things based on the original iPhones resolution, you’ll have some cleaning up to do.

Just one more thing …

When working on a 17″ MacBook Pro, I found that the simulator crowds the dock on the bottom of the desktop. So you may want to consider moving your dock to the side.


Tags: ,

Jan 18

As I’ve mentioned before, my background is in software testing. I’ve made a career out of making software crash. You may think your app is flawless – that it can’t possibly crash. Wait until you hand it off to someone else. If that person is sitting right next to you, you may be able to walk through the steps in the debugger in the simulator and figure out what happened. But if that person is someone who downloaded your app from iTunes – or worse, a whole lot of people who bought your app from iTunes – and it only crashes on the actual device – then things will get a lot more complicated.

Reproducing a Crash

When a professional software tester, such as myself, crashes a product our job is to try to reproduce the crash and write up detailed steps on how we did it. But when the product crashes in the field customers aren’t so helpful. Quite often they will tell you that they have no idea how the product crashed, or they don’t have the patience to try to reproduce it. Hopefully you will at least be able to get them to give you a log. What log is that you may wonder? Is this something that you build in to your product? It could be – but I’m talking about the crash log that the iPhone will (hopefully) generate automatically and replicate to their desktop via iTunes.

A great article on where the crash logs are located and how to process them can be found here:

http://www.anoshkin.net/blog/2008/09/09/iphone-crash-logs/

In that article you will find out how to use a very useful tool: symbolicatecrash . Pay close attention to the instructions on generating and keeping a dSYM file when you release an app.

The article also contains a few useful links:

For debugging apps you may find these articles useful as well:

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:

Oct 26

I noticed that I still get a lot of hits from sites containing old articles on Flash / ActionScript that I wrote. I’m sure those people are wondering where the Flash stuff went. I haven’t given up on Flash. I just decided to make my main focus the iPhone. In fact in my spare time I’ve been working on porting some turtle graphics code that I wrote in ActionScript to the iPhone. I’ve also been experimenting with writing apps for both the iPhone and Adobe AIR – which is a subject for another post.

Anyway, as you can see from the video, soon you will be able to create apps in Flash – then export directly for the iPhone. As the video points out, they aren’t talking about a browser plugin (since there isn’t one). These are native self-contained apps that you can post and buy from iTunes.

For more info checkout these links:

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 17

If you scroll to the bottom of my previous post you will notice that I’ve updated it with a new section:

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

Going forward, I will attempt to post all sample code using git. Usually this means that it will come wrapped in a full Xcode project.

I have several reasons for using git:

  • I don’t have to wrestle with creating a zip file and uploading it to some obscure server somewhere
  • You don’t have to wrestle with downloading a zip file and expanding it somewhere
  • You don’t need an account to copy my public repositories
  • All you have to do is enter one line in a terminal window
  • You instantly end up with a local copy of the repository that you can copy and branch any which way you like
  • It forces you to think about source control :)
  • If something is missing, I can just checkin new stuff and do a refresh
  • If I hack an existing project, I can just add a branch instead of forcing you and I to deal with multiple projects

So if you still haven’t installed git on your Mac yet, I suggest you go back and read my post on the subject again.  Also if you have, you may want to read it again.  I’ve added a few more links and info on filtering out certain files to keep your repository from getting cluttered.

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: , ,

Sep 04

Over the years I’ve worked with many source control management systems (CVS, SVN, SourceSafe, PerForce).   The problem is that they are oriented towards a central server.  Git on the other hand is a new and improved type of source control.  It can work with a server - in fact, it can work with many servers.  But it can also work with just your collaborators workstation across the hall – bypassing the need for a server completely.

If you are just working by yourself (as many iPhone developers are) you can run it on your local workstation – even without a network connection.  If later you want to make your repository available to others — or just create an offsite backup — you can either open up a share on your workstation or clone a repository to a server.

Instead of going into too much detail, I’m just going to provide links to resources for you to look at.  You can decide for yourself if it is right for you:

  • Git Home Page
  • http://progit.org/book/ - A free Web-based version of the book Pro Git.
  • Pro Git on Apress – You can help the author of Pro Git out by buying a PDF or hard-copy.
  • Git for OS X – This is what you’ll need to install it on your Mac.  In my case I had to make sure that I got the latest Intel / Leopard DMG file.
  • Installing Git on Leopard – For the most part you will just want the Git for OS X installer – but see the notes on setting up your config.
  • Gitx – GitX is a git GUI made for Mac OS X
  • Heroku tips on Git – Heroku is a cloud-based host for Rails apps – but you may find some of the generic tips and links useful.
  • Windows Installer – Why would an iPhone developer need a Windows installer?  It could make it easy to trade *.cpp files with your PC.  Plus you can use it for your other projects.  Git is platform independent.
  • GitHub.com – Free hosting for your open source projects.  Paid options also available.
  • Unfuddle - Also offers free hosting.  Unlike GitHub, the free plan can be private.

Installing on the Mac

When you download Git for OS X the DMG file should open up.  Launch the installer.

After installation completes, open up a terminal window and configure it via the command line (substitute your name and e-mail address):

git config --global user.name "John Doe"
git config --global user.email "john.doe@gmail.com"

When that is done type the following:

git config --list

Ignoring Files

On a PC, you would see several items listed.  On the Mac, apparently you have to set those yourself.  See the link Installing Git on Leopard for what other globals you have to set.  Be sure to configure the ability to use a .gitignore file for filtering what goes into the repository.

The link suggests that you put your .gitignore file in your user directory.  I prefer to put the file in the root of the project.  Yes, it is a hassle to always copy the file from project to project.  But it helps you make sure that whomever else downloads your repository has the right filter too.

So I would configure my globals like this:

git config --global core.excludesfile .gitignore

For iPhone SDK projects, here is what I have for a .gitignore file:

# xcode noise
build/*
*.xcclassmodel/*
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
 
# old skool
.svn
 
# osx noise
.DS_Store
profile

Here is what I have for .gitattributes – which also sits in the root directory of the project:

.pbxproj -crlf -diff -merg

For more info, see these links:

http://shanesbrain.net/2008/7/9/using-xcode-with-git

http://rudifa.wordpress.com/2009/05/19/git-xcode-and-projectlocker/

Hidden Files

One thing to note is that when you create a local repository it goes into a subfolder called “.git” under your projects directory.  On Windows you can see this folder.  It’s hidden by default on the Mac.  The same is true for .gitignore and .gitattributes.  If you are in a Terminal window, you can view hidden files using the -A flag for ls.  Example:

apple$ ls -l -A

If you would like to be able to view hidden files and folders in Finder enter this line from a Terminal window:

defaults write com.apple.Finder AppleShowAllFiles YES

The hidden files won’t appear until you restart Finder.  For more info, see this link. (Note: their steps for restarting Finder didn’t work for me – I just rebooted).

Git Hosting

If you do decide to setup a Git server, it can be a bit of work.  To get around that problem at work I just use file shares.  The good thing about Git is that it can refer to a server based reposiory either via http:// or file:/// (example:  file:///\\mywinsever\git\myproject.git – note that there are three slashes after “file:” ).  If that makes no sense to you now, don’t worry.  It will once you come up to speed.

If you want to share your project with the world, hosts like GitHub.com and Unfuddle can also save you a lot of work.   These sites are new and suffer from a lack of documentation.  But if you don’t mind being on the bleeding edge you may find them useful.

Tags: