Custom Gesture in cocos2d

Posted by Lewis on Game Development See other posts from Game Development or by Lewis
Published on 2012-06-29T22:25:56Z Indexed on 2012/06/30 3:24 UTC
Read the original article Hit count: 419

I've found a little tutorial that would be useful for my game:

http://blog.mellenthin.de/archives/2012/02/13/an-one-finger-rotation-gesture-recognizer/

But I can't work out how to convert that gesture to work with cocos2d, I have found examples of pre made gestures in cocos2d, but no custom ones, is it possible?

EDIT STILL HAVING PROBLEMS WITH THIS:

I've added the code from Sentinel below (from SO), the Gesture and RotateGesture have both been added to my solution and are compiling. Although In the rotation class now I only see selectors, how do I set those up? As the custom gesture found in that project above looks like:

header file for custom gesture:

#import <Foundation/Foundation.h>
#import <UIKit/UIGestureRecognizerSubclass.h>

@protocol OneFingerRotationGestureRecognizerDelegate <NSObject>
@optional
- (void) rotation: (CGFloat) angle;
- (void) finalAngle: (CGFloat) angle;
@end

@interface OneFingerRotationGestureRecognizer : UIGestureRecognizer
{
    CGPoint midPoint;
    CGFloat innerRadius;
    CGFloat outerRadius;
    CGFloat cumulatedAngle;
    id <OneFingerRotationGestureRecognizerDelegate> target;
}

- (id) initWithMidPoint: (CGPoint) midPoint
            innerRadius: (CGFloat) innerRadius
            outerRadius: (CGFloat) outerRadius
                 target: (id) target;
- (void)reset;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

@end

.m for custom gesture file:

#include <math.h>

#import "OneFingerRotationGestureRecognizer.h"

@implementation OneFingerRotationGestureRecognizer

// private helper functions
CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2);
CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA,
                                   CGPoint endLineA,
                                   CGPoint beginLineB,
                                   CGPoint endLineB);

- (id) initWithMidPoint: (CGPoint) _midPoint
            innerRadius: (CGFloat) _innerRadius
            outerRadius: (CGFloat) _outerRadius
                 target: (id <OneFingerRotationGestureRecognizerDelegate>) _target
{
    if ((self = [super initWithTarget: _target action: nil]))
    {
        midPoint    = _midPoint;
        innerRadius = _innerRadius;
        outerRadius = _outerRadius;
        target      = _target;
    }
    return self;
}

/** Calculates the distance between point1 and point 2. */
CGFloat distanceBetweenPoints(CGPoint point1, CGPoint point2)
{
    CGFloat dx = point1.x - point2.x;
    CGFloat dy = point1.y - point2.y;
    return sqrt(dx*dx + dy*dy);
}

CGFloat angleBetweenLinesInDegrees(CGPoint beginLineA,
                                   CGPoint endLineA,
                                   CGPoint beginLineB,
                                   CGPoint endLineB)
{
    CGFloat a = endLineA.x - beginLineA.x;
    CGFloat b = endLineA.y - beginLineA.y;
    CGFloat c = endLineB.x - beginLineB.x;
    CGFloat d = endLineB.y - beginLineB.y;

    CGFloat atanA = atan2(a, b);
    CGFloat atanB = atan2(c, d);

    // convert radiants to degrees
    return (atanA - atanB) * 180 / M_PI;
}

#pragma mark - UIGestureRecognizer implementation

- (void)reset
{
    [super reset];
    cumulatedAngle = 0;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesBegan:touches withEvent:event];

    if ([touches count] != 1)
    {
        self.state = UIGestureRecognizerStateFailed;

        return;
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesMoved:touches withEvent:event];

    if (self.state == UIGestureRecognizerStateFailed) return;

    CGPoint nowPoint  = [[touches anyObject] locationInView: self.view];
    CGPoint prevPoint = [[touches anyObject] previousLocationInView: self.view];

    // make sure the new point is within the area
    CGFloat distance = distanceBetweenPoints(midPoint, nowPoint);
    if (   innerRadius <= distance
        && distance    <= outerRadius)
    {
        // calculate rotation angle between two points
        CGFloat angle = angleBetweenLinesInDegrees(midPoint, prevPoint, midPoint, nowPoint);

        // fix value, if the 12 o'clock position is between prevPoint and nowPoint
        if (angle > 180)
        {
            angle -= 360;
        }
        else if (angle < -180)
        {
            angle += 360;
        }

        // sum up single steps
        cumulatedAngle += angle;

        // call delegate
        if ([target respondsToSelector: @selector(rotation:)])
        {
            [target rotation:angle];
        }
    }
    else
    {
        // finger moved outside the area
        self.state = UIGestureRecognizerStateFailed;
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
{
    [super touchesEnded:touches withEvent:event];

    if (self.state == UIGestureRecognizerStatePossible)
    {
        self.state = UIGestureRecognizerStateRecognized;

        if ([target respondsToSelector: @selector(finalAngle:)])
        {
            [target finalAngle:cumulatedAngle];
        }
    }
    else
    {
        self.state = UIGestureRecognizerStateFailed;
    }

    cumulatedAngle = 0;
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];

    self.state = UIGestureRecognizerStateFailed;
    cumulatedAngle = 0;
}

@end

Then its initialised like this:

// calculate center and radius of the control
    CGPoint midPoint = CGPointMake(image.frame.origin.x + image.frame.size.width / 2,
                                   image.frame.origin.y + image.frame.size.height / 2);
    CGFloat outRadius = image.frame.size.width / 2;

    // outRadius / 3 is arbitrary, just choose something >> 0 to avoid strange 
    // effects when touching the control near of it's center
    gestureRecognizer = [[OneFingerRotationGestureRecognizer alloc] initWithMidPoint: midPoint
                                                                innerRadius: outRadius / 3 
                                                                outerRadius: outRadius
                                                                     target: self];
    [self.view addGestureRecognizer: gestureRecognizer];

The selector below is also in the same file where the initialisation of the gestureRecogonizer:

- (void) rotation: (CGFloat) angle
{
    // calculate rotation angle
    imageAngle += angle;
    if (imageAngle > 360)
        imageAngle -= 360;
    else if (imageAngle < -360)
        imageAngle += 360;

    // rotate image and update text field
    image.transform = CGAffineTransformMakeRotation(imageAngle *  M_PI / 180);
    [self updateTextDisplay];
}

I can't seem to get this working in the RotateGesture class can anyone help me please I've been stuck on this for days now.

SECOND EDIT:

Here is the users code from SO that was suggested to me:

Here is projec on GitHub: SFGestureRecognizers It uses builded in iOS UIGestureRecognizer, and don't needs to be integrated into cocos2d sources. Using it, You can make any gestures, just like you could, if you whould work with UIGestureRecognizer. For example: I made a base class Gesture, and subclassed it for any new gesture:

//Gesture.h
@interface Gesture : NSObject <UIGestureRecognizerDelegate>
{
    UIGestureRecognizer *gestureRecognizer;
    id delegate;
    SEL preSolveSelector;
    SEL possibleSelector;
    SEL beganSelector;
    SEL changedSelector;
    SEL endedSelector;
    SEL cancelledSelector;
    SEL failedSelector;

    BOOL preSolveAvailable;

    CCNode *owner;
}
- (id)init;

- (void)addGestureRecognizerToNode:(CCNode*)node;
- (void)removeGestureRecognizerFromNode:(CCNode*)node;

-(void)recognizer:(UIGestureRecognizer*)recognizer;

@end

//Gesture.m
#import "Gesture.h"

@implementation Gesture

- (id)init
{
    if (!(self = [super init]))
        return self;

    preSolveAvailable = YES;

    return self;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)recognizer shouldReceiveTouch:(UITouch *)touch
{
    //! For swipe gesture recognizer we want it to be executed only if it occurs on the main layer, not any of the subnodes ( main layer is higher in hierarchy than children so it will be receiving touch by default ) 
    if ([recognizer class] == [UISwipeGestureRecognizer class])
    {
        CGPoint pt = [touch locationInView:touch.view];
        pt = [[CCDirector sharedDirector] convertToGL:pt];

        for (CCNode *child in owner.children)
        {
            if ([child isNodeInTreeTouched:pt]) 
            {
                return NO;
            }
        }
    }

    return YES;
}

- (void)addGestureRecognizerToNode:(CCNode*)node
{
    [node addGestureRecognizer:gestureRecognizer];
    owner = node;
}

- (void)removeGestureRecognizerFromNode:(CCNode*)node
{
    [node removeGestureRecognizer:gestureRecognizer];
}

#pragma mark - Private methods

-(void)recognizer:(UIGestureRecognizer*)recognizer
{
    CCNode *node = recognizer.node;
    if (preSolveSelector && preSolveAvailable)
    {
        preSolveAvailable = NO;
        [delegate performSelector:preSolveSelector withObject:recognizer withObject:node];
    }

    UIGestureRecognizerState state = [recognizer state];

    if (state == UIGestureRecognizerStatePossible && possibleSelector)
    {

        [delegate performSelector:possibleSelector withObject:recognizer withObject:node];
    }

    else if (state == UIGestureRecognizerStateBegan && beganSelector)
        [delegate performSelector:beganSelector withObject:recognizer withObject:node];

    else if (state == UIGestureRecognizerStateChanged && changedSelector)
        [delegate performSelector:changedSelector withObject:recognizer withObject:node];

    else if (state == UIGestureRecognizerStateEnded && endedSelector)
    {
        preSolveAvailable = YES;
        [delegate performSelector:endedSelector withObject:recognizer withObject:node];
    }


    else if (state == UIGestureRecognizerStateCancelled && cancelledSelector)
    {
        preSolveAvailable = YES;
        [delegate performSelector:cancelledSelector withObject:recognizer withObject:node];
    }


    else if (state == UIGestureRecognizerStateFailed && failedSelector)
    {
        preSolveAvailable = YES;
        [delegate performSelector:failedSelector withObject:recognizer withObject:node];
    }

}

@end
Subclass example:

//RotateGesture.h
#import "Gesture.h"

@interface RotateGesture : Gesture

- (id)initWithTarget:(id)target 
    preSolveSelector:(SEL)preSolve
    possibleSelector:(SEL)possible 
       beganSelector:(SEL)began 
     changedSelector:(SEL)changed 
       endedSelector:(SEL)ended
   cancelledSelector:(SEL)cancelled 
      failedSelector:(SEL)failed;

@end

//RotateGesture.m
#import "RotateGesture.h"

@implementation RotateGesture

- (id)initWithTarget:(id)target 
    preSolveSelector:(SEL)preSolve
    possibleSelector:(SEL)possible 
       beganSelector:(SEL)began 
     changedSelector:(SEL)changed 
       endedSelector:(SEL)ended
   cancelledSelector:(SEL)cancelled 
      failedSelector:(SEL)failed  
{
    if (!(self = [super init]))
        return self;

    preSolveSelector = preSolve;
    delegate = target;
    possibleSelector = possible;
    beganSelector = began;
    changedSelector = changed;
    endedSelector = ended;
    cancelledSelector = cancelled;
    failedSelector = failed;

    gestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(recognizer:)];
    gestureRecognizer.delegate = self;

    return self;
}

@end

Use example:

- (void)addRotateGesture
{
    RotateGesture *rotateRecognizer = [[RotateGesture alloc] initWithTarget:self
                             preSolveSelector:@selector(rotateGesturePreSolveWithRecognizer:node:)
                             possibleSelector:nil
                                beganSelector:@selector(rotateGestureStateBeganWithRecognizer:node:)
                              changedSelector:@selector(rotateGestureStateChangedWithRecognizer:node:)
                                endedSelector:@selector(rotateGestureStateEndedWithRecognizer:node:)
                            cancelledSelector:@selector(rotateGestureStateCancelledWithRecognizer:node:)
                               failedSelector:@selector(rotateGestureStateFailedWithRecognizer:node:)];
    [rotateRecognizer addGestureRecognizerToNode:movableAreaSprite];
}

I dont understand how to implement the custom gesture code at the start of this post into the rotateGesture class which is a subclass of the gesture class written by the SO user.

Any ideas please? When I get 6 more rep I'll add a bounty to this.

© Game Development or respective owner

Related posts about cocos2d-iphone

Related posts about cocos2d