*sigh* + lol
Adding UIGestureRecognizer support in cocos2d
(87 posts) (33 voices)-
Posted 1 year ago #
-
Ok, I'm not sure if this is the best place to post this question, but I am having an issue with my code using UIPinchGestureRecognizer. Basically I can pinch and zoom just fine using UIPinchGestureRecognizer if I only do it once. However, if I have zoomed in, say to zoombase.scale=0.5, then let go, and try to re-pinch, the UIPinchGestureRecognizer rescales the zoombase layer without taking into account what its previous scale was.
Here is HelloWorldScene.h:
#import "cocos2d.h" @interface HelloWorld : CCLayer { CCColorLayer *zoombase; } +(id) scene; @endHere is the code for HelloWorldScene.m:
static const float MIN_SCALE = 0.5; static const float MAX_SCALE = 1.0; #import "HelloWorldScene.h" @implementation HelloWorld +(id) scene { CCScene *scene = [CCScene node]; HelloWorld *layer = [HelloWorld node]; [scene addChild: layer]; return scene; } -(id) init { if( (self=[super init] )) { CGSize size = [[CCDirector sharedDirector] winSize]; zoombase = [CCColorLayer layerWithColor:ccc4(128,0,0,128)]; [self addChild:zoombase]; CCSprite *cocosguy = [CCSprite spriteWithFile:@"Icon.png"]; cocosguy.position = ccp(size.width/2, size.height/2); [zoombase addChild:cocosguy]; UIPinchGestureRecognizer *pinchGestureRecognizer = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchFrom:)] autorelease]; [[[CCDirector sharedDirector] openGLView] addGestureRecognizer:pinchGestureRecognizer]; } return self; } // Zoom - (void)zoomLayer:(float)zoomScale { NSLog(@"zoombase scale: %f and scale from the gesture: %f\n",zoombase.scale, zoomScale); if (zoomScale <= MIN_SCALE) { zoomScale = MIN_SCALE; } if (zoomScale >= MAX_SCALE) { zoomScale = MAX_SCALE; } zoombase.scale = zoomScale; } - (void)handlePinchFrom:(UIPinchGestureRecognizer *)recognizer { float zoomScale = [recognizer scale]; [self zoomLayer:zoomScale]; } - (void) dealloc { [super dealloc]; } @endStrange behavior (from Debugger console):
1) This is ok because it rescales to 0.581244 just fine:
2011-02-22 19:16:02.675 UIPinch_troubleshoot[2031:207] zoombase scale: 0.581244 and scale from the gesture: 0.581244
2) Here is the problem. In between this line and the one above, I let go and re-pinched: 2011-02-22 19:16:07.281 UIPinch_troubleshoot[2031:207] zoombase scale: 0.581244 and scale from the gesture: 1.008862Let me know if any of this is unclear, and thank you in advance for any help/insight!
KristiPosted 1 year ago # -
Off the top of my head this sounds a little like this;
After panning for the translation, it resets the translation on the recognizer to zero, because otherwise the translation is cumulative, and you just want the difference each time.
But with pinching rather than panning. See Rays full article at;
http://www.raywenderlich.com/2343/how-to-drag-and-drop-sprites-with-cocos2d
Posted 1 year ago # -
Every time a new gesture starts, the UIGestureRecognisers generally reset their values to zero.
It's not explicitly stated in the docs for UIPinchGestureRecognizer, but it's assumed. All the values you can query a UIGestureRecognizer for are "since the gesture started". It's probably in the UIGestureRecognizer docs somewhere.
What you probably want to do is apply the new scale to the current scale in your
handlePinch:method, then reset the UIPinchGestureRecognizer's scale to 0. Then the nexthandlePinch:call will see the scale since the last call, not the scale since the gesture started. So it won't matter if the user stopped and started the gesture since or not.Posted 1 year ago # -
Thanks guys! I had read Ray's article, but couldn't quite wrap my head around how that would translate (ha!) to scaling a layer with pinch-zoom. In case anyone else has this issue in the future, here is a solution that makes the pinch-zoom work more intuitively (to me at least):
// Zoom - (void)zoomLayer:(float)zoomScale { NSLog(@"zoombase scale: %f and scale from the gesture: %f\n",zoombase.scale, zoomScale); if ((zoombase.scale*zoomScale) <= MIN_SCALE) { zoomScale = MIN_SCALE/zoombase.scale; } if ((zoombase.scale*zoomScale) >= MAX_SCALE) { zoomScale = MAX_SCALE/zoombase.scale; } zoombase.scale = zoombase.scale*zoomScale; } - (void)handlePinchFrom:(UIPinchGestureRecognizer *)recognizer { float zoomScale = [recognizer scale]; [self zoomLayer:zoomScale]; zoomScale = 1; }Posted 1 year ago # -
Since this thread is active again and nobody responded to my question :( I'm going to repost with high hopes! Would really like to get this working and I don't know why its not.
I'm having problems getting the code to work - I've applied the patch and using the following code in a subclass of CCLayer:
- (id) init { if( (self=[super init] )) { CCGestureRecognizer* recognizer; recognizer = [CCGestureRecognizer CCRecognizerWithRecognizerTargetAction:[[[UIPinchGestureRecognizer alloc]init] autorelease] target:self action:@selector(onPinchZoom:node:)]; [self addGestureRecognizer:recognizer]; self.isTouchEnabled = YES; } return self; } // This method is called upon recognizing a gesture by the UIPinchGestureRecognizer - (void)onPinchZoom:(UIPinchGestureRecognizer *)recognizer node:(CCNode *)node { // get the scale from the recognizer NSLog(@"pinch!!"); self.scale = [recognizer scale]; }I don't see any logging so the callback isn't being fired. I've tried this same methodology in the parent of the layer and no dice. What am I doing wrong? I've commented out the registerWithTouchDispatcher, ccTouchBegan and ccTouchEnded functions completely. Using 0.99.5. ANY help or thoughts would greatly be appreciated! Been banging my head about this for weeks now.
Posted 1 year ago # -
I posted the memory leak I was thinking of to github on 9-29-10 and I haven't noticed a memory leak since then. The leak was a circular reference between gesture recognizer and CCNode, which would match the leak detected with addGestureRecognizer. Has anyone been experiencing a leak since that fix?
Posted 1 year ago # -
@goofball
Your function signature does't look correct. You always set it to take a UIGestureRecognizer and you cast it to the appropriate type.
Something like this...
- (void) onPinchZoom:(UIGestureRecognizer*)recognizer node:(CCNode*)node { UIPinchGestureRecognizer* pinch = (UIPinchGestureRecognizer*)recognizer; // get the scale from the recognizer NSLog(@"pinch!!"); self.scale = [pinch scale]; }Posted 1 year ago # -
Hey, first I want to thank you for your code =)
Second I have a question (maybe its a little silly but I'm new with cocos and iphone):
I have a CCLayer with 2 recognizers for drag and pinch... It works great.
And I have a CCSprite in that layer with a drag recognizer that works... not so great.
if I remove the drag from the layer, the sprite drags fine, but when the 2 drags recognizers are present the layer and the sprite moves at the same time... I know that is an expected behavior since the 2 recognizers are working fine and at the same time, but I need to block te layer drag/recognizer if I'm moving the sprite and I dont see a good and non destructive way to do it.
Can you help me?
Thanks.
Posted 1 year ago # -
Ive not solved this problem with two gesture recognisers, as my drag recorgniser is not implemented as a recogniser, but I had the same problem with the recogniser on the layer also picking up the event when the sprite is being moved.
You can implement
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touchon your layer which should return NO to the layer drag recogniser if your sprite is being dragged (I just checked if the touch location was inside the sprite). The following code is obviously lifted from my app and there are obviously methods in there which you wont have, but the principle remains (and I also dont need to check which recogniser im returning YES and NO to as I only have one):
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { // dont process recogniser if any photos are being touched for (PhotoSprite *photo in _photos) { if ([photo containsTouchLocation:touch]) { return NO; } } return YES; }Posted 1 year ago # -
I changed the default implementation for shouldRecognizeSimultaneouslyWithGestureRecognizer to always return true. With cocos only using the 1 view it just didn't work trying to have multiple gesture recognizers without doing this. You can change your layer to support the UIGestureRecognizerDelegate protocol and set it to your drag gesture recognizers delegate property. In your layer you would just have this function
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return NO; }You can find more information on how these work here
Posted 1 year ago # -
Hi xemus,
this code is great, very useful indeed!
Any plans to make this working on v1.0.0? I haven't tried it yet on that release...thanks, Roland
Posted 1 year ago # -
thanks for you guys helpful information!
Posted 1 year ago # -
Hey xemus, thank you very much for this code.. I have it 50% working; I am adding UIPanGestureRecognizers to sprites that are in two separate layers.
Whichever layer has the highest z-order when added to the scene receives the touch events, and the layer with the lower z-order does not receive any events. Whenever I switch the z-order around the reverse happens.. so everything seems to be setup correctly, its just I have to figure out how to let my touches 'pass through' the top most layer...
I have set cancelsTouchesInView to NO on the recognizers in each layer, yet im still not able to get both layers to work at the same time.
Please can anyone help out... thanks
Posted 1 year ago # -
Just as a follow up: I fixed this temporarily by commenting out the following check in CCGestureRecognizer#gestureRecognizer:shouldReceiveTouch method (line 142 onwards):
// if( [child isNodeInTreeTouched:pt] ) // { // rslt = NO; // break; // }Now the sprites with gesture recognizers in both layers receive the touch events..
but im scared this is going to have side effects somewhere later down the line, so am still looking for a better solution.Posted 1 year ago # -
thanks for sharing it.
Posted 1 year ago # -
I'm having trouble with gesture recognition on multiple layers. Basically if the top layer is the one with gesture recognisers, then they work perfectly, but if I add another layer, then the gestures don't propogate down layer (but touches do if they aren't swallowed).
Code's here: http://www.cocos2d-iphone.org/forum/topic/16001 (I didn't want to fill up this thread).
Posted 1 year ago # -
Does anyone know if the existing patch works with 1.0?
Posted 1 year ago # -
First off, xemus, thanks a ton for posting this, it's really gonna help a lot of people (including me).
Second, ej, I don't think this will work with 1.0. The reason being is because I am using 1.0 and I'm getting some weird warnings, not noticed in the source version of the project, that turn out to crash the project. For example, one error in the addRecognizers method:
[node addGestureRecognizer:recognizer]; - 'CCNode' may not respond to '-addGestureRecognizer'
There were also about 6 warnings in the actual CCGestureRecognizer class.
Xemus, I know you must be sick of patching this, but if you patched it so it would work with 1.0, I think a lot of people would appreciate it (including me XD).
Thank you
Posted 1 year ago # -
could you make a fix for the patch to work with cocos2d 1.0?
xemus thank you very much for sharing this!
Posted 1 year ago # -
I posted a cleaned up version of this on a github fork. If you check out the touches test app it shows use of both touch systems together. https://github.com/xemus/cocos2d-iphone
Posted 11 months ago # -
Thank you so much for this handy class. I'm new to cocos2d so apologies if I misunderstand how I'm supposed to be using your gesture recognizers, but it seems to me that the isPointInArea method is not accounting for changes to anchorPoint. For example, I tried adding a sprite that has a UITapGestureRecognizer to my layer with only a different anchorPoint and the change to anchorPoint prevents the sprite from registering the appropriate taps:
TappableSprite *touchable = [[TappableSprite alloc] initWithFile:@"sprite.png"]; touchable.position = ccp(200.0f, 110.0f); TappableSprite *untouchable = [[TappableSprite alloc] initWithFile:@"sprite.png"]; untouchable.anchorPoint = ccp(1.5f, 1.5f); untouchable.position = ccp(200.0f, 390.0f); [self addChild:touchable]; [self addChild:untouchable];A small change to CCNode.m (line 373) seems to do the trick:
rect.origin = CGPointMake( -(rect.size.width/2.0f), -(rect.size.height/2.0f) );
to
rect.origin = CGPointMake( -(rect.size.width*self.anchorPoint.x), -(rect.size.height*self.anchorPoint.y) );Posted 9 months ago # -
hello, thanks xemus for your work.
I'm using it in some of my applications. Unfortunately I have problems on some ipad 2, the drag seems strangely reversed. No one has found a similar problem? I repeat, only a few ipad2 ... inexplicablePosted 8 months ago # -
I managed to get this working with cocos2d 1.0.0, and it works quite nicely, except I have a small issue when I use it in conjunction with mobilebros's CCPanZoomController, more specifically, when I'm zoomed in. I am using a long press recognizer, and my screen orientation is set to landscape.
I use [recognizer locationInView:recognizer.view] to get the touch point in the view, and then use [node convertToNodeSpace:touchLocation] to get the space in the node.
When I am at a 1x zoom (so the scale of my node is 1x as well), long pressing in the upper left gives me about 0,0.
However, when I zoom in to 2x, the coordinates are skewed based on where I'm panned at. If I have the node's lower left point in the lower left of the screen, then the upper left point is converted to a node space of 0,0 instead of 0,160 like I'd expect; then when I pan to put the upper left of the node into the upper left of the screen, the upper left is convereted to 0,160 and the lower left is converted to 0,320.
If anyone has any insight on this, it would help. I am not sure what I am doing wrong here. This only seems to be affected the y coordinate, the x coordinate appears to be converted with no issues at all.
Posted 6 months ago # -
Actually, I found a solution to my problem. While I couldn't find where in the CCGestureRecognizer code it was modifying the touch coordinates to match the screen orientation, I did realize that the y coordinate's 0 position was flipped (instead of the bottom being 0, it was using the top as 0), and that was confusing CCNode's convertToNodeSpace. By taking the window size and subtracting the touch y coordinate from that, it corrected my issue.
Posted 6 months ago #
Reply
You must log in to post.