Detecting touch events in cocos2d-iphone | Ganbaru Games

<div class="posterous_bookmarklet_entry">
  > <div>

In the previous tutorial I wrote about scenes and layers, the basic structural classes of a cocos2d app. In this next installment, I’m going to talk about putting some actual content into your scenes and responding to user input.

If we’re interested in getting user input, first we should create something that responds to that input. In most cases, this will be a sprite object. A sprite is usually a representation of an entity in your game system, such as the player, an enemy, or a power-up. When you create a new sprite, you give the sprite an image as a parameter. After you add the sprite to a containing layer, that image then appears on the screen, and you can move it around by manipulating properties of the sprite object. A simple example:

1.CCSprite *mySprite = [CCSprite spriteWithFile:@``"Icon.png"``];
2.[mySprite setPosition:ccp(160, 240)];
3.[self addChild:mySprite z:1];

This is the easiest way to get an image onto the screen. Once you’ve created a sprite, you can transform it in various ways.

1.[mySprite setPosition:ccp(30, 30)];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ``// Change position
2.[mySprite setScale:2.0];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ``// Make 2x bigger
3.[mySprite setRotation:180.0];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ``// Rotate

Wait, this post was supposed to be about detecting user touches, right? OK, let’s create an example app that will use the ubiquitous “pinch” gesture to scale a sprite that’s displayed in the center of the screen.

Create a new project using the regular cocos2d template. I named mine “TouchExample.” Open up the Classes group in the Groups & Files sidebar, click on the HelloWorldScene.h file, then modify the class declaration as follows:

01.@interface HelloWorld : CCLayer
03.&nbsp;&nbsp;&nbsp;&nbsp;CCSprite *mySprite;
06.@property (nonatomic, retain) CCSprite *mySprite;
08.// returns a Scene that contains the HelloWorld as the only child
09.+(id) scene;

Basically what you’re doing here is creating a reference to a sprite object that can be accessed from any method in the HelloWorld layer. That means you can create the sprite in your “init” method, then reference it and move it around in a different method later. Next, open the HelloWorldScene.m file. Navigate to the “init” method, and replace the code there:

01.// Creates "setters" and "getters" for this sprite
02.@synthesize mySprite;
04.- (id)init
06.&nbsp;&nbsp;&nbsp;&nbsp;if ((self = [super init]))
08.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ask director the the window size
09.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CGSize size = [[CCDirector sharedDirector] winSize];
11.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Set layer to respond to touch events
12.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[self setIsTouchEnabled:TRUE];
14.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create sprite and add to layer
15.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mySprite = [CCSprite spriteWithFile:@``"Icon.png"``];
16.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mySprite setPosition:ccp(size.width / 2, size.height / 2)];
17.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[self addChild:mySprite];
19.&nbsp;&nbsp;&nbsp;&nbsp;return self;

What we’re doing here is telling the layer that we want it to respond to touches. Then we create a sprite using the app icon and slap it in the center of the screen. Next, add the following methods after “init.” When you called the “setIsTouchEnabled” method of the layer in the init method, the following three methods are available to be overridden with your own logic.

01.- (``void``)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
03.&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@``"Touches began!"``);
06.// Override the "ccTouchesMoved:withEvent:" method to add your own logic
07.- (``void``)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
09.&nbsp;&nbsp;&nbsp;&nbsp;// This method is passed an NSSet of touches called (of course) "touches"
10.&nbsp;&nbsp;&nbsp;&nbsp;// "allObjects" returns an NSArray of all the objects in the set
11.&nbsp;&nbsp;&nbsp;&nbsp;NSArray *touchArray = [touches allObjects];
13.&nbsp;&nbsp;&nbsp;&nbsp;// Only run the following code if there is more than one touch
14.&nbsp;&nbsp;&nbsp;&nbsp;if ([touchArray count] &gt; 1)
16.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// We're going to track the first two touches (i.e. first two fingers)
17.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create "UITouch" objects representing each touch
18.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UITouch *fingerOne = [touchArray objectAtIndex:0];
19.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UITouch *fingerTwo = [touchArray objectAtIndex:1];
21.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Convert each UITouch object to a CGPoint, which has x/y coordinates we can actually use
22.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CGPoint pointOne = [fingerOne locationInView:[fingerOne view]];
23.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CGPoint pointTwo = [fingerTwo locationInView:[fingerTwo view]];
25.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// The touch points are always in "portrait" coordinates
26.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// You will need to convert them if in landscape (which we are)
27.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pointOne = [[CCDirector sharedDirector] convertToGL:pointOne];
28.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pointTwo = [[CCDirector sharedDirector] convertToGL:pointTwo];
30.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Get the distance between the touch points
31.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float distance = ``sqrt``(``pow``(pointOne.x - pointTwo.x, 2.0) + ``pow``(pointOne.y - pointTwo.y, 2.0));
33.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Scale the distance based on the overall width of the screen (multiplied by a constant, just for effect)
34.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;float scale = distance / [CCDirector sharedDirector].winSize.width * 5;
36.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Set the scale factor of the sprite
37.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[mySprite setScale:scale];
41.- (``void``)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
43.&nbsp;&nbsp;&nbsp;&nbsp;NSLog(@``"Touches ended!"``);

You can see that there are three phases we can use to get info about user touches: touchesBegan, touchesMoved, and touchesEnded. Right now we’re only concerned about what happens when touches move around the screen, so the other two methods just have logging statements in them to prove that they’re firing.

The ccTouchesMoved: method is automatically given an NSSet of UITouch objects when it is called. You can see in the commented code how those objects are converted down into CGPoint structs that contain regular Cartesian coordinates, which can then be used in a meaningful way. If your game is set up to run in landscape mode (or can toggle between portrait and landscape), you’ll need to send the CGPoint coordinates to the CCDirector to be converted to the current orientation. However, if your game is portrait-only (like, ahem, Nonogram Madness), you can omit that step.

Once the x/y values of the two touch points are obtained, they get plugged into the Pythagorean theorem to find the distance between them. That distance is scaled by the total screen width, then applied to the sprite as a scaling factor.

The last step that needs to be taken is that we need to tell the app that it should recognize multiple touches. To do that, open up TouchExampleAppDelegate.m and find where the OpenGL view is created (do a search for “EAGLView *glView”). After the long initialization, type:

1.[glView setMultipleTouchEnabled:TRUE];

Build & run the project in the iOS simulator, and hold down the Option key to make two touch points with your mouse. You can see that the sprite scales up and down based on the distance between the touches.

Congrats! You have the basics of getting user input in your cocos2d app. Play around with the project and see what other sorts of ways you can manipulate the sprite. One problem with this example is that the sprites’ scale factor is reset each time you touch the screen again. An interesting reader exercise might be to retain the scale factor, so that the interaction feels more “natural.” I’ve attached my solution (which is probably needlessly complex) in an Xcode project file.

View Comments · Posted by nathan in Programming · cocos2d, iOS, programming, touchscreen, tutorial, user input

                        <div>[<span>←</span> Nonogram Madness 1.1](</div>
                        <div>[My Photoshop Replacement <span>→</span>](</div>

blog comments powered by Disqus

Wow… multitouch with cocos2d-iphone

comments powered byDisqus