Custom Gesture in cocos2d
- by Lewis
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.