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, 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.
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];
}
... and you need only implement needed selectons.
EDIT
Here is mine angle calculations. In my case, I just use a button, and it's two last positions.
- (void)onRotatorDraggedWithArgs:(EventArgs *)e
{
CGPoint buttonPosition = [e.params CGPointValue];
float angle = atanf((buttonPosition.x - currentCastPosition.x) / (buttonPosition.y - currentCastPosition.y));
if (buttonPosition.y > currentCastPosition.y)
angle -= M_PI_2;
else
angle += M_PI_2;
currentCastAngle = CC_RADIANS_TO_DEGREES(angle);
[worldState.activeCastIcon rotateToAngle:currentCastAngle animated:NO];
currentPosition = buttonPosition;
}
Related
I'm trying to create a UIPinchGestureRecognizer that fails if the user pinches out (moves there fingers apart). I know there is a really easy way to do this by just taking the scale of t he recogniser in the action method and if it is larger than 1, set a flag and ignore all the future calls. However, this does not work for me, I have other gesture recognisers that require this pinch recogniser to fail, so I need it to fail properly when the user pinches in the wrong direction.
I have attempted to subclass UIPinchGestureRecognizer:
#implementation BHDetailPinchGestureRecogniser {
CGFloat initialDistance;
}
#pragma mark - Object Lifecycle Methods
- (id)initWithTarget:(id)target action:(SEL)action {
self = [super initWithTarget:target action:action];
if (self) {
initialDistance = NSNotFound;
}
return self;
}
#pragma mark - UIGestureRecogniser Methods
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)recogniser {
if ([recogniser isKindOfClass:[UIPinchGestureRecognizer class]]) {
return [self.delegate gestureRecognizerShouldBegin:self];
} else return false;
}
- (void)reset {
[super reset];
initialDistance = NSNotFound;
}
#pragma mark - Touch Handling Methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (touches.count >= 2) {
if (self.state == UIGestureRecognizerStatePossible) {
// Keep hold of the distance between the touches
NSArray *bothTouches = [touches allObjects];
CGPoint location1 = [(UITouch *)bothTouches[0] locationInView:self.view];
CGPoint location2 = [(UITouch *)bothTouches[1] locationInView:self.view];
initialDistance = [self distanceBetweenPoint:location1 andPoint:location2];
}
} else {
// Fail if there is only one touch
self.state = UIGestureRecognizerStateFailed;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"Moved. (tc, %lu) (state, %i) (id, %f)", (unsigned long)touches.count, self.state, initialDistance);
if (touches.count >= 2) {
if (self.state == UIGestureRecognizerStatePossible) {
if (initialDistance != NSNotFound) {
// Get the new distance and see if is is larger or smaller than the original
NSArray *bothTouches = [touches allObjects];
CGPoint location1 = [(UITouch *)bothTouches[0] locationInView:self.view];
CGPoint location2 = [(UITouch *)bothTouches[1] locationInView:self.view];
NSLog(#"Checking distance between points.");
if ([self distanceBetweenPoint:location1 andPoint:location2] < initialDistance - 3.f) {
NSLog(#"Began");
self.state = UIGestureRecognizerStateBegan;
} else if ([self distanceBetweenPoint:location1 andPoint:location2] > initialDistance) {
NSLog(#"Failed");
self.state = UIGestureRecognizerStateFailed;
}
}
} else {
self.state = UIGestureRecognizerStateChanged;
}
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (self.state == UIGestureRecognizerStatePossible) self.state = UIGestureRecognizerStateFailed;
else [super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
self.state = UIGestureRecognizerStateCancelled;
}
#pragma mark - Helper Methods
- (CGFloat)distanceBetweenPoint:(CGPoint)point1 andPoint:(CGPoint)point2 {
return sqrtf(powf(point1.x - point2.x, 2) + powf(point1.y - point2.y, 2));
}
#end
What I have realised that it is not that simple, it will need to handle multiple touches, sometimes touches moved only has one touch so it will need to keep hold of the touches, one touch might end and another start and this still want to be part of the pinch. It seems like I am loosing the build in functionality of the pinch recogniser when all I want to do is make it fail if the user pinches in the wrong direction.
Is there a simpler way to achieve this behaviour without completely rewriting UIPinchGestureRecognizer? It is probably worth mentioning that I have no control over the other recognisers that I want to fail (they are deep in the bowls of a PSPDFViewController (third party library)).
How about using the gesture recogniser's delegate to help?
Set a delegate with the following implementation of gestureRecognizerShouldBegin
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let pinch = gestureRecognizer as? UIPinchGestureRecognizer {
return pinch.scale < 1
}
return true
}
I am newcomer to cocos2d v3.1
How to drag and drop the Physics body in cocos2d v3.1.
and then how to check the Collision & Detection between two Physics Body's.
Thank you in advance
First, you need a class named CCTouchJoint.
CCTouchJoint.h
#import "Box2D.h"
#interface CCTouchJoint : NSObject
{
#public
b2MouseJoint *mouseJoint;
UITouch *touch;
}
#property (assign) b2MouseJoint *mouseJoint;
#property (nonatomic, retain) UITouch *touch;
- (id)initLocal:(UITouch *)touch withMouseJoint:(b2MouseJoint *)mouseJoint;
+ (id)touch:(UITouch *)touch withMouseJoint:(b2MouseJoint *)mouseJoint;
// Public methods
/**
* Destroy the touch joint in the Box2d world.
*/
- (void)destroyTouchJoint;
#end
CCTouchJoint.mm
#import "CCTouchJoint.h"
#implementation CCTouchJoint
#synthesize mouseJoint;
#synthesize touch;
- (void)dealloc
{
[touch release];
[super dealloc];
}
- (id)initLocal:(UITouch *)_touch withMouseJoint:(b2MouseJoint *)_mouseJoint
{
if ((self = [super init]))
{
self.touch = _touch;
mouseJoint = _mouseJoint;
}
return self;
}
+ (id)touch:(UITouch *)_touch withMouseJoint:(b2MouseJoint *)_mouseJoint
{
return [[self alloc] initLocal:_touch withMouseJoint:_mouseJoint];
}
#pragma mark -
#pragma mark CCTouchJoint Public Methods
- (void)destroyTouchJoint
{
if (mouseJoint != NULL)
{
mouseJoint->GetBodyA()->GetWorld()->DestroyJoint(mouseJoint);
}
}
#pragma mark CCTouchJoint Private Methods
#end
Second. self.touchEnabled = YES; You need
NSMutableArray *touchJointList;
b2Body *groundBody and b2Body *b;
and this touch code:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouches = [event allTouches];
for(UITouch *touch in allTouches)
{
CGPoint location = [touch locationInView:touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 worldLoc = b2Vec2(ptm(location.x), ptm(location.y));
for (b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetType() == b2_dynamicBody)
for (b2Fixture *f = b->GetFixtureList(); f; f = f->GetNext())
{
// Hit!
if (f->TestPoint(worldLoc))
{
/// Mouse joint definition
b2MouseJointDef md;
md.bodyA = groundBody;
md.bodyB = b;
md.target = worldLoc;
md.maxForce = 3000.0 * b->GetMass();
md.dampingRatio = 5;
md.frequencyHz = 60;
// Joint of bodys
b2MouseJoint *m_touchJoint;
m_touchJoint = (b2MouseJoint *)world->CreateJoint(&md);
CCTouchJoint *tj = [CCTouchJoint touch:touch withMouseJoint:m_touchJoint];
[touchJointList addObject:tj];
b->SetAwake(true);
}
break;
}
}
}
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (CCTouchJoint *tj in touchJointList)
{
if([tj.touch phase] == UITouchPhaseMoved)
{
// Update if it is moved
CGPoint location = [tj.touch locationInView:tj.touch.view];
location = [[CCDirector sharedDirector] convertToGL:location];
b2Vec2 worldLocation = b2Vec2(ptm(location.x), ptm(location.y));
tj.mouseJoint->SetTarget(worldLocation);
}
}
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouches = [event allTouches];
NSMutableArray *discardedItems = [NSMutableArray array];
for(UITouch *touch in allTouches)
{
for (CCTouchJoint *tj in touchJointList)
{
if (tj.touch == touch)
{
// Defensive programming - assertion
NSAssert([tj isKindOfClass:[CCTouchJoint class]], #"node is not a touchJoint!");
// If safe - loop through
if ([tj.touch phase] == UITouchPhaseEnded)
{
[discardedItems addObject:tj];
[tj destroyTouchJoint];
[tj release];
}
}
}
}
[touchJointList removeObjectsInArray:discardedItems];
}
- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[touchJointList removeAllObjects];
}
REMEMBER:Changing md.dampingRatio and md.frequencyHz will affect on behavior of mouse joint.
Try the Below link for collision detection. It will be helpful for you.
http://www.cocos2d-x.org/wiki/Chapter_5_-_How_to_Detect_the_Collisions
Is there an easy way to do the following?
I want to split a view into 4 via two diagonal lines:
So the upper triangle area will correspond to "up".
The other 3 will correspond to "left", "down", "right" respectively.
What would be the easiest or the most efficient way of doing this?
I had considered creating 4 triangle shapes and detecting touches inside them, but maybe this will be unnecessary?
Context: For moving a sprite in a map.
Thanks!
After my comment, I wanted to mock up a simple implementation for myself, so, here's a simple full screen joystick that will determine cardinal-like directions based on
touch position relative to the center of the screen.
(FullScreenJoystick)
I'm still using cocos2d-iphone 1.1 , so.. I'm not sure if there are any mods necessary for v2
FSJoy.h
#import "cocos2d.h"
// Up, Down, Left, Right - directions
typedef enum
{
DIR_NONE = 0,
DIR_UP,
DIR_LEFT,
DIR_DOWN,
DIR_RIGHT
} ULDR;
// UpLeft, UpRight, DownLeft, DownRight - boundaries
typedef enum
{
UL = 135,
UR = 45,
DL = 225,
DR = 315
} ULDR_BOUNDS;
#interface FSJoy : CCLayer
{
CGPoint origin;
float angleCurrent;
BOOL isActive;
}
#property CGPoint origin;
#property float angleCurrent;
#property BOOL isActive;
#end
FSJoy.m
#import "FSJoy.h"
#implementation FSJoy
#synthesize origin, angleCurrent, isActive;
-(id) init
{
if( (self = [super init]) )
{
self.isTouchEnabled = YES;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
angleCurrent = 0.0f;
origin = ccp(screenSize.width / 2.0f, screenSize.height / 2.0f);
isActive = NO;
}
return self;
}
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:(INT_MIN - 4) swallowsTouches:YES];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchCurrent = [touch locationInView:[touch view]];
touchCurrent = [[CCDirector sharedDirector] convertToGL:touchCurrent];
angleCurrent = [self determineAngleFromPoint:origin toPoint:touchCurrent];
[self determineDirectionFromAngle: angleCurrent];
isActive = YES;
return YES; // full screen controller, so always return YES
}
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchCurrent = [touch locationInView:[touch view]];
touchCurrent = [[CCDirector sharedDirector] convertToGL:touchCurrent];
angleCurrent = [self determineAngleFromPoint:origin toPoint:touchCurrent];
[self determineDirectionFromAngle: angleCurrent];
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
isActive = NO;
}
-(void) ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
[self ccTouchEnded:touch withEvent:event];
}
-(float) determineAngleFromPoint:(CGPoint)from toPoint:(CGPoint)to
{
float angle = ccpToAngle(ccpSub(to, from));
if (angle <= 0.0f)
{
angle += M_PI * 2.0f;
}
angle = CC_RADIANS_TO_DEGREES(angle);
// NSLog(#"angle is %f", angle);
return angle;
}
-(ULDR) determineDirectionFromAngle:(float) angle
{
ULDR dir = DIR_NONE;
dir = ((angle >= UR) && (angle <= UL)) ? DIR_UP : dir;
dir = ((angle >= DL) && (angle <= DR)) ? DIR_DOWN : dir;
dir = ((angle > UL) && (angle < DL)) ? DIR_LEFT : dir;
dir = ((angle > DR) || (angle < UR)) ? DIR_RIGHT : dir;
NSLog(#"direction is %d", dir);
return dir;
}
#end
Usage is simply to add the Joystick to the scene/layer:
FSJoyTest.h
#import "cocos2d.h"
#interface FSJoyTest : CCLayer
#end
FSJoyTest.m
#import "FSJoyTest.h"
#import "FSJoy.h"
#implementation FSJoyTest
-(id) init
{
if( (self=[super init]) )
{
[self addChild: [FSJoy node]];
}
return self;
}
#end
Just an idea :
Create an image in the size of the view above (don't add it to any view just store it somewhere). The image file will contain the four triangles like you desire but color each triangle with a different color (no gradients ! :))
When you tap the view take the touch location coordinates (convert it to image coordinates if needed) and test the pixel color in the image in that same location. The color will tell you which triangle was pressed.
I want to ask some interesting thing.
I want my app can action when a custom combined gestures occur.
For example:
call a action(/method) when user swipe left, top, left without the finger leave on the screen.
How can i make this custom gesture?
and second question is can i swipe upper left( like " / " this direction)?
How to make this gesture?
can anyone one help me? please! thanks
Well, conceptually you need to subclass UIGestureRecognizer, do some position tracking, and make use of the fact that after importing UIGestureRecognizerSubclass.h the gestures state property becomes read/write to move all the gears around yourself. As and example, I'll include a basic prototype for a diagonal gesture that I tried to build a long time ago. (It needs a few kinks worked out, but overall, it works.)
.H
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface MMDiagnoalSwipeGestureRecognizer : UIGestureRecognizer
#property (nonatomic, readwrite) BOOL shouldReverseYDelta;
#property (nonatomic, readwrite) CGFloat tolerance;
#property (nonatomic, readonly) CGFloat angleOfSwipe;
#end
.M
#import "MMDiagnoalSwipeGestureRecognizer.h"
#interface MMDiagnoalSwipeGestureRecognizer ()
#property (nonatomic, readwrite) CGPoint startingPoint;
#end
#implementation MMDiagnoalSwipeGestureRecognizer
- (id)initWithTarget:(id)target action:(SEL)action {
self = [super initWithTarget:target action:action];
if (self) {
_shouldReverseYDelta = NO;
_tolerance = 30.0f;
}
return self;
}
- (CGFloat)angleOfGestureFromPoint:(CGPoint)start toEndPoint:(CGPoint)end {
CGFloat deltaY = (_shouldReverseYDelta) ? end.y - start.y : start.y - end.y;
CGFloat deltaX = end.x - start.x;
_angleOfSwipe = atan2f(deltaY, deltaX) * 180.0f / M_PI;
return _angleOfSwipe;
}
- (CGFloat)diagonalDistanceFromPoint:(CGPoint)start toEndPoint:(CGPoint)end {
CGFloat deltaX = fabsf(start.x - end.x);
CGFloat deltaY = fabsf(start.y - end.y);
CGFloat hypotenuse = powf(deltaX, 2.0f) + powf(deltaY, 2.0f);
return sqrtf(hypotenuse);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (touches.count > 1) {
if (self.state == UIGestureRecognizerStatePossible) {
self.state = UIGestureRecognizerStateFailed;
}
}else{
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
UITouch *touch = (UITouch *)obj;
_startingPoint = [touch locationInView:self.view];
}];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[touches enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
UITouch *touch = (UITouch *)obj;
CGPoint endPoint = [touch locationInView:self.view];
CGFloat angle = [self angleOfGestureFromPoint:_startingPoint toEndPoint:endPoint];
if (self.state == UIGestureRecognizerStatePossible) {
if ([self angleIsWithinDiagonalTolerance:angle] == YES) {
if ([self diagonalDistanceFromPoint:_startingPoint toEndPoint:endPoint] >= 20.0f) {
self.state = UIGestureRecognizerStateRecognized;
}
}else{
self.state = UIGestureRecognizerStateFailed;
}
}
}];
}
- (BOOL)angleIsWithinDiagonalTolerance:(CGFloat)angle
{
// NSLog(#"%s",__PRETTY_FUNCTION__);
NSAssert1(_tolerance < 45.0f, #"DiagonalSwipeGestureRecognizer Error: tolerance property must be set to a value less than 45.0f. Otherwise, the gesture will be detected at any angle. If you don't care and want the swipe to be recognized at any angle, remove the NSAssert call from - %s.", __PRETTY_FUNCTION__);
// NSAssert(_tolerance < 45.0f, #"DiagonalSwipeGestureRecognizer Error: tolerance property must be set to a value less than 45.0f. Otherwise, the gesture will be detected at any angle. If you don't care and want the swipe to be recognized at any angle, remove the NSAssert call from -[MMDiagnoalGestureRecognizer angleIsWithinDiagonalTolerance:].");
if (angle >= 45.0f - _tolerance && angle <= 45.0f + _tolerance) {
return YES;
}else if (angle <= - (45.0f - _tolerance) && angle >= - (45.0f + _tolerance)) {
return YES;
}else if (angle >= 135.0f - _tolerance && angle <= 135.0f + _tolerance) {
return YES;
}else if (angle <= - (135.0f - _tolerance) && angle >= - (135.0f + _tolerance)) {
return YES;
}else{
return NO;
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
self.state = UIGestureRecognizerStateChanged;
}
- (void)reset {
//don't call, will be called automatically.
}
#end
I just implemented this:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint translation = [panGestureRecognizer translationInView:someView];
return fabs(translation.y) > fabs(translation.x);
}
(As outlined here.)
But if the user pans vertically just over the diagonal it will start. How do I make the tolerance much more strict for what it considers vertical?
Basically, the image below describes what I'm after. The first diagram is what it detects now, anything within that area, and the second is what I want it to do.
You can use atan2f given x and y values to calculate the angle from vertical. For example, to start the gesture if the angle is less than 4 degrees from vertical, you can do something like this:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gesture {
CGPoint translation = [gesture translationInView:gesture.view];
if (translation.x != 0 || translation.y != 0) {
CGFloat angle = atan2f(fabs(translation.x), fabs(translation.y));
return angle < (4.0 * M_PI / 180.0); // four degrees, but in radians
}
return FALSE;
}
Detecting pure vertical gestures, I assume that translation.x == 0 then.
You should as well, check the correct answer from the post you referenced. Where he compares the previous location with the current one. You can create the sensibility. You can check my project, for example to see that, where I use this sensibility to define, when an action is valid (less or equal than the sensibility) or invalid (bigger than the sensibility). Check the MOVEMENT_SENSIBILITY inside the RPSliderViewController.m.
I've written a UIGestureRecognizer subclass for that purpose once. It only tracks the vertical translation. Maybe that helps you. You can use it as any other gesture recognizer, just set the threshold and track the translation in the it's target's action method.
VerticalPanGestureRecognizer.h
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface VerticalPanGestureRecognizer : UIGestureRecognizer
#property (assign, nonatomic)float translation;
#property (assign, nonatomic)float offsetThreshold;
#end
VerticalPanGestureRecognizer.m
#import "VerticalPanGestureRecognizer.h"
#interface VerticalPanGestureRecognizer ()
{
CGPoint _startPoint;
}
#end
#implementation VerticalPanGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if ([touches count] > 1) {
self.state = UIGestureRecognizerStateFailed;
}
else
{
_startPoint = [[touches anyObject] locationInView:self.view];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.state == UIGestureRecognizerStateFailed || self.state == UIGestureRecognizerStateCancelled) {
return;
}
CGPoint currentLocation = [[touches anyObject] locationInView:self.view];
CGPoint translation;
translation.x = currentLocation.x - _startPoint.x;
translation.y = currentLocation.y - _startPoint.y;
if (self.state == UIGestureRecognizerStatePossible)
{
//if the x-translation is above our threshold the gesture fails
if (fabsf(translation.x) > self.offsetThreshold)
self.state = UIGestureRecognizerStateFailed;
//if the y-translation has reached the threshold the gesture is recognized and the we start sending action methods
else if (fabsf(translation.y) > self.offsetThreshold)
self.state = UIGestureRecognizerStateBegan;
return;
}
//if we reached this point the gesture was succesfully recognized so we now enter changed state
self.state = UIGestureRecognizerStateChanged;
//we are just insterested in the vertical translation
self.translation = translation.y;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//if at this point the state is still 'possible' the threshold wasn't reached at all so we fail
if (self.state == UIGestureRecognizerStatePossible)
{
self.state = UIGestureRecognizerStateFailed;
}
else
{
CGPoint currentLocation = [[touches anyObject] locationInView:self.view];
CGPoint translation;
translation.x = _startPoint.x - currentLocation.x;
translation.y = _startPoint.y - currentLocation.y;
self.translation = translation.y;
self.state = UIGestureRecognizerStateEnded;
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
self.state = UIGestureRecognizerStateCancelled;
}
- (void)reset
{
[super reset];
_startPoint = CGPointZero;
}
#end