Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Dragging a CCNode in Cocos2D-Swift

Save for later
  • 6 min read
  • 21 Jan 2015

article-image

 In this article by Ben Trengrove, author of the book Cocos2D Game Development Essentials, we will see how can we update our sprite position according to the touch movement.

(For more resources related to this topic, see here.)

Very often in development with Cocos2d you will want the ability to drag a node around the screen. It is not a built in behavior but it can be easily coded. To do it you will need to track the touch information. Using this information you will move the sprite to the updated position anytime the touch moves.

Lets get started.

  1. Add a new Boolean property to your private interface.
    @interface HelloWorldScene ()
    @property (nonatomic, assign) BOOL dragging;
    @end
  2. Now, add the following code to the touchBegan method.

    -(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
      CGPoint touchLoc = [touch locationInNode:self];
      if (CGRectContainsPoint(_sprite.boundingBox, touchLoc)) {
        self.dragging = YES;
        NSLog(@"Start dragging");
      }
    }

  3. Add a touchMoved method with the following code.
    - (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
      CGPoint touchLoc = [touch locationInNode:self];
      if (self.dragging) {
        _sprite.position = touchLoc;
      }
    }
  4. What is being done in these methods is first you check to see if the initial touch was inside the sprite. If it was, we set a Boolean to say that the user is dragging the node. They have in effect picked up the node.
  5. Next in the touchMoved method, it is as simple as if the user did touch down on the node and move, set the new position of the node to the touch location.
  6. Next we just have to implement the letting go of the sprite. This is done in touchEnded.
  7. Implement the touchEnded method as follows.
    - (void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
      self.dragging = NO;
    }
  8. Now, if you build and run the app you will be able to drag around the sprite. There is one small problem however, if you don't grab the sprite in its center you will see that the node snaps its center to the touch. What you really want to happen is just move from where on the node it was touched. You will make this adjustment now.
  9. To make this fix you are going to have to calculate the offset on the initial touch from the nodes center point. This will be stored and applied to the final position of the node in touchMoved.

    dragging-ccnode-cocos2d-swift-img-0

  10. Add another property to your private interface.
    @property (nonatomic, assign) CGPoint dragOffset;
  11. Modify your touchBegan method to the following:
    -(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
      CGPoint touchLoc = [touch locationInNode:self];
      CGPoint touchOffset = [touch locationInNode:_sprite];
      if (CGRectContainsPoint(_sprite.boundingBox, touchLoc)) {
        self.dragging = YES;
        NSLog(@"Start dragging");
        self.dragOffset = touchOffset;
      }
    }

    Notice that using the locationinnode method, you can calculate the position of the touch relative to the node. This information is only useful if the touch was indeed inside of the node so you only store it if that is the case.

    Unlock access to the largest independent learning library in Tech for FREE!
    Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
    Renews at £15.99/month. Cancel anytime
  12. Now, modify your touchMoved method to the following:
    - (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
      CGPoint touchLoc = [touch locationInNode:self];
      //Check if we are already dragging
      if (self.dragging) {
        CGPoint offsetPosition = ccpSub(touchLoc, self.dragOffset);
    //Calculate an offset to account for the anchor point        
    CGPoint anchorPointOffset = CGPointMake(_sprite.anchorPoint.x * _sprite.boundingBox.size.width, _sprite.anchorPoint.y * _sprite.boundingBox.size.height);
    //Add the offset and anchor point adjustment together to get the final position
        CGPoint positionWithAnchorPoint = ccpAdd(offsetPosition, anchorPointOffset);
        _sprite.position = positionWithAnchorPoint;
      }
    }

    The offset position is subtracted from the touch location using the Cocos2d convenience function ccpSub. CcpSub subtracts a point from another point.

    dragging-ccnode-cocos2d-swift-img-1

  13. Using the anchor point and size of the sprite, an adjustment is calculated to account for different anchor points.
  14. Once these two points have been calculated, they are added together to create a final sprite position.
  15. Build and run the app now, you will now have a very natural dragging mechanic.

For reference, here is the complete scene.

@interface HelloWorldScene ()
@property (nonatomic, assign) BOOL dragging;
@property (nonatomic, assign) CGPoint dragOffset;

@end
- (id)init
{
  // Apple recommend assigning self with supers return value
  self = [super init];
  if (!self) return(nil);

  // Enable touch handling on scene node
  self.userInteractionEnabled = YES;

  // Create a colored background (Dark Grey)
  CCNodeColor *background = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0.2f green:0.2f blue:0.2f alpha:1.0f]];
  [self addChild:background];

  // Add a sprite
  _sprite = [CCSprite spriteWithImageNamed:@"Icon-72.png"];
  _sprite.position  = ccp(self.contentSize.width/2,self.contentSize.height/2);
  _sprite.anchorPoint = ccp(0.5, 0.5);
  [self addChild:_sprite];

  // Create a back button
  CCButton *backButton = [CCButton buttonWithTitle:@"[ Menu ]" fontName:@"Verdana-Bold" fontSize:18.0f];
  backButton.positionType = CCPositionTypeNormalized;
  backButton.position = ccp(0.85f, 0.95f); // Top Right of screen
  [backButton setTarget:self selector:@selector(onBackClicked:)];
  [self addChild:backButton];

  // done
return self;
}
// -----------------------------------------------------------------------
#pragma mark - Touch Handler
// -----------------------------------------------------------------------

-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
  CGPoint touchLoc = [touch locationInNode:self];
  CGPoint touchOffset = [touch locationInNode:_sprite];

  if (CGRectContainsPoint(_sprite.boundingBox, touchLoc)) {
    self.dragging = YES;
    NSLog(@"Start dragging");
    self.dragOffset = touchOffset;
  }
}

- (void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
  CGPoint touchLoc = [touch locationInNode:self];

  if (self.dragging) {
    CGPoint offsetPosition = ccpSub(touchLoc, self.dragOffset);
    CGPoint anchorPointOffset = CGPointMake(_sprite.anchorPoint.x * _sprite.boundingBox.size.width, _sprite.anchorPoint.y * _sprite.boundingBox.size.height);
    CGPoint positionWithAnchorPoint = ccpAdd(offsetPosition, anchorPointOffset);
    _sprite.position = positionWithAnchorPoint;
  }
}

- (void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
  self.dragging = NO;
}

Summary

In this article, we saw how to update your sprite position according to the touch movement.

Resources for Article:


Further resources on this subject: