Dragging multiple sprites in Cocos2d

On September 6, 2010, in cocos2d, Game development, by admin

One of the questions that pops up often when talking to people new at using cocos2d is how to drag multiple sprites. There are lots of ways to achieve this, some more messy code-wise than others. This article will show you how to delegate touches to the sprites themselves, and not have to worry about it in the layer.

Click here to download the complete project.

This simple project consists of 2 classes :
HelloWorld” : The CCLayer where we will have our sprites, plus a button to add more of them
DragSprite” : Our CCSprite subclass that will handle touches itself

Let’s start with the init of the DragSprite class:

{code type=php}
-(id) init
{
if( (self=[super init] )) {

// Button to add more sprites to the layer
CCMenuItemFont *add = [CCMenuItemFont itemFromString:@”Add Sprite” target:self selector:@selector(addSprite:)];
add.position=ccp(80,30);
CCMenu *menu = [CCMenu menuWithItems:add,nil];
[self addChild:menu];
menu.position=ccp(0,0);

}
return self;
}
{/code}

The first thing we do is simple. We create a CCMenuItemFont, a button to add more sprites to the layer. As you can see, when pressed, the button will call a selector called addSprite. Here is the code for this selector :

{code type=php}
-(void) addSprite:(id)sender{
//
// Add a sprite and position it randomly on the screen
//
DragSprite *s = [DragSprite spriteWithFile:@”smile.png”];
s.position=ccp(arc4random() % 480,arc4random() % 320);
[self addChild:s];
}
{/code}

This is all the code for our layer! As you can see, this just creates a simple sprite (don’t forget DragSprite inherits CCSprite). It places it randomly on the X and Y axis of the device’s screen. Also, note that we don’t have any arrays keeping track of sprites. We don’t need one because the sprites will keep track of themselves, and handle their own touches. They implement the CCTargetedTouchDelegate protocol.

Now, let’s get to the meat of this article, the DragSprite class. I will again start with the init, which consists of a single line of code :

{code type=php}
-(id) init{
if((self=[super init])){
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
return self;
}
{/code}

Here, we tell the CCTouchDispatcher singleton that this sprite (self) wants to receive touch events. Note that it will receive ALL touch events, not just the ones touching the it. This is why we need to set priorities and check for a position match. In our case, all the sprites will have the same priority. swallowsTouches means that once we accept the touch (return YES on the touch began method), no other nodes will receive the touch. If we set this to NO, all touch delegates will be informed of the touch, wether others have accepted it or not. This can be useful for dragging multiple sprites at once for example.

Next, now that we added the node to the CCTouchDispatcher, we need to implement the touch methods. Here’s the first one, ccTouchBegan:

{code type=php}
– (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];

if([self isTouchOnSprite:touchPoint]){
isDrag=YES;
whereTouch=ccpSub(self.position, touchPoint);
return YES;
}

return NO;
}
{/code}

The first 2 lines are just to get the touch in cocos2d coordinates. Then, we check if the touch is on the sprite. Note that the method isTouchOnSprite is not from cocos2d. It will be detailed next in this article. If the method returns YES, it means that the touch is on the sprite. Since it is, we return YES. if it is not, we return NO and the 2 other touch method, touchMoved and touchEnded will not be called. The touch will then be passed to all other nodes implementing the touch delegate.

You will also see that I set another variable, whereTouch. This is a simple CGPoint declared in the header file. Its goal is to know exactly where the touch is on the sprite, so when we move it, it won’t auto center to your finger. For example, if you grab it at it’s top left corner, it will be dragged from that point. You’ll understand when running the included project.

Now here’s the isTouchOnSprite method, which checks if the touch is on the sprite (remember that all nodes that receive touches receive ALL touches, wether it actually touches the concerned node or not).

{code type=php}
-(BOOL) isTouchOnSprite:(CGPoint)touch{
if(CGRectContainsPoint(CGRectMake(self.position.x – ((self.contentSize.width/2)*self.scale), self.position.y – ((self.contentSize.height/2)*self.scale), self.contentSize.width*self.scale, self.contentSize.height*self.scale), touch))
return YES;
else return NO;
}
{/code}

The CGRectContainsPoint is a simple function that takes 2 parameters : a CGRect and a CGPoint. It returns YES if the point is in the rect and no if it is not.

CGRectMake is a method that creates a CGRect, and takes 4 parameters : x, y, width and height. If CGRectContainsPoint returns YES, we return YES to our ccTouchBegan method to actually register the touch and receive the touchMoved and touchEnded calls, where we will move the sprite.

Now, here’s the ccTouchMoved selector. This will be called when we accepted the touch (returned YES on ccTouchBegan) and the finger moved. This is where we will move the sprite to the new touch position and add the whereTouch difference so the sprite doesn’t auto center to the finger’s position.

{code type=php}
– (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
self.position=ccpAdd(touchPoint,whereTouch);
}
{/code}

As you can see, all we’re doing is changing the position of the sprite (self) to the addition of the new touch location + touch location on the sprite. This means that if we drag it from a corner, as soon as we move, the sprite wont center itself on the touch. If this is what you want, remove whereTouch from this project and it will work like that.

In ccTouchEnded, for the case of this project, we put nothing. We could have not even implemented it and it would work fine.

{code type=php}
– (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{

}
{/code}

I hope this article helped you better understand how touches work in cocos2d. Doing it this method, you could use multiple fingers and drag multiple sprites at the same time. This is the true way to do multitouch sprite dragging in Cocos2d.

Tagged with:  

Archives

All entries, chronologically...

Free WordPress Theme