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