nathandemick.com

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:

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:

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:

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.

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:

[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.

Comments

[...] moving. To move it, we’ll have to implement the three touch-detecting methods talked about in my previous tutorial, and determine how the user input will move the ship. The scheme I decided upon is pinch & [...]

Infotechswati wrote on April 21, 2011:

nice tutorial

Dom wrote on August 06, 2011:

Hey
great tutorial, really helping. just wondering though, how would i go about adding or implementing a touch event to move the sprite around the screen with a single touch aswell as being able to pinch as per example.
i thought something like this...
if ([touchArray count] < 2)
{
UITouch *fingerOne = [touchArray objectAtIndex:0];
CGPoint location = [fingerOne locationInView:fingerOne.view];
[mySprite setPosition:ccp(location.x , location.y )];
}
it's not working though, am i way off the mark?, I'm a complete noob. any help would be really appreciated

Hmm, that looks OK off the top of my head. One thing that's helpful for me is to debug with NSLog statements. It's not the most efficient, but it's pretty easy to do. In this case you could put a log statement like this inside your if clause, to make sure that it's being evaluated in the way you expect: NSLog(@"Touch position: %i, %i", location.x, location.y);
That might help you figure out if the problem is with the array of touch objects or with the sprite.

Dom wrote on August 08, 2011:

hey, thanks so much for replying, really appreciated.... i managed to get it working, i put the if statement directly after "NSArray *touchArray = [touches allObjects];" and it works, instead of at the end of the previous if statements (idiot i am) although I've hit another small issue, the touch seem inverted, if i move the "finger" up in simulator the sprite moves down and the same the opposite way round. left and right is absolutely fine.... i'll go over the code and see if i can spot the issue.
thanks again for your help.

You know what, I think that I might have mislead you with one of the comments in my example code. OpenGL coordinates and UIKit coordinates have an inverted y-axis. So you should still run the touch object through the cocos2d Director's converter method like so:
location = [[CCDirector sharedDirector] convertToGL:location];
I wrongly put in a comment that you only have to do that to convert from portrait -> landscape orientation. In reality, the touch object is provided by UIKit, and is then being passed to cocos2d (OpenGL), so you have to convert it. Sorry about the confusion, that was my mistake :/

Dom wrote on August 10, 2011:

i don't think you mislead me, infact i only vaguely remember skimming across that comment. I'm extremely new to cocos2d (and programming in general) and am just trying to pick up the basics before undertaking anything major. i tryed your method to convert but couldn't get it to work, i eventually managed to get the sprite moving the right way by using this. btw everything's running in portrait.
CGPoint location = [self convertTouchToNodeSpace:fingerOne];
although it seems to make pinching less accurate, as the "fingers" in the simulator appear in random locations. most probably my fault ;)

Asdadadad wrote on June 12, 2012:

Great Tutorial! It gave me some basic gasp on touch events!
Thanks
regards

DMike wrote on March 22, 2013:

Sir, God Bless You. I have been scouring the internet looking for an answer to this question and yours was the only one that was succinct and intelligible. Most other posts were sending me to added classes that did not seem to work or added so much extra code as to be ridiculous. Thank you once again.

Douglas_Lemus_13062 wrote on June 18, 2013:

hello, I have a question, need to do a memory game, well my question is how I can select a CCSprite and then you can select another?

[…] Detecting touch events in cocos2d-iphone | Ganbaru Games. In the previous tutorial I wrote about scenes and layers, the basic structural classes of a cocos2d app. […]

Nonogram Madness!

Get addicted to a new type of puzzle — nonograms — in Nonogram Madness!

Shikaku Madness!

Another Japanese puzzle game — cover the puzzle grid with rectangles,
using the provided clues as guidance!

Let's Build Bridges!

Connect all the islands together, using the number of bridges shown
on each one. It's harder than it looks!

Consume!

If you're in the market for imported or pre-owned
(or pre-owned imported) games, check out Play Asia.
I've gotten a lot of good junk from 'em. If you buy something
using the link above, I get whole pennies of referral credit!