Detect Touch en CCSprite

Soy nuevo en cocos2d, así que disculpe mi ignorancia, pero me gustaría saber cómo detectar cuándo se tocó un sprite y llamar a un método cuando lo tocó.

Definí y agregué mi sprite así:

CCSprite *infoButton = [CCSprite spriteWithFile: @"info.png"]; [infoButton setPosition:CGPointMake(450, 290)]; [menuBG addChild:infoButton]; 

He seguido varios resources, pero han sido muy vagos, la mayoría de los cuales el sprite se estableció en su propia class.

Gracias por adelantado.

¿Por qué no usa CCMenuItemImage?

  CCMenuItemImage* info = [CCMenuItemImage itemFromNormalImage:@"info.png" selectedImage:@"info.png" target:self selector:@selector(pressed:)]; CCMenu* menu = [CCMenu menuWithItems:info, nil]; menu.position = ccp(450,290); [menuBG addChild:menu]; 

y otra function siempre que el usuario presionó el button ..

 -(void)pressed:(id)sender { // whatever you would like to do here... } 

En Cocos2D regular:

 -(void) ccTouchesBegan:(NSSet*)touches withEvent:(id)event { CCDirector* director = [CCDirector shanetworkingDirector]; UITouch* touch = [touches anyObject]; CGPoint touchLocation = [touch locationInView:director.openGLView]; CGPoint locationGL = [director convertToGL:touchLocation]; CGPoint locationInNodeSpace = [infoButton convertToNodeSpace:locationGL]; CGRect bbox = CGRectMake(0, 0, infoButton.contentSize.width, infoButton.contentSize.height); if (CGRectContainsPoint(bbox, locationInNodeSpace)) { // code for when user touched infoButton sprite goes here ... } } 

Para demostrar cuánto Kobold2D simplifica esto sobre el enfoque de Cocos2D:

 -(void) update:(ccTime)delta { KKInput* input = [KKInput shanetworkingInput]; if ([input isAnyTouchOnNode:infoButton touchPhase:KKTouchPhaseBegan]) { // code for when user touched infoButton sprite goes here ... } } 

La solución depende de su architecture de código. Para los elementos del menu, use la variante xuanweng . Alternativamente, puede verificar la intersección del punto de contacto con los límites de sprite en el método ccTouchBegan de la capa primaria. Necesita transformar el punto de contacto en el espacio de capa (en caso común, esta transformación es identidad) y verifique CGRectContainsPoint ([sprite boundingBox], touchPos)

Hice este detector de events personalizado hace un time

Este es el file CCNode + eventss.h (el file de encabezado)

 // // CCNode+events.h // Save the world´s // // Created by Sebastian Winbladh on 2013-10-14. // Copyright (c) 2013 Sebastian Winbladh. All rights reserved. // #import "cocos2d.h" #import <objc/runtime.h> //We are using CCLayer so we can capture events that occurs on top of it @interface EventLayer : CCLayer @property (nonatomic,assign) NSMutableArray *nodes; @property (nonatomic,assign) void (^callback)(NSArray*nodeArray,NSSet*touches,NSString *event); +(id)shanetworkingEventLayer:(CCNode *)on callback:(void(^)(NSArray*nodeArray,NSSet*touches,NSString *event))block node:(NSArray *)addNode; @end @interface CCNode (props) @property (nonatimic,assign) id rotationCX; @property (nonatomic,assign) id rotationCY; @property (nonatomic,assign) id scaleCX; @property (nonatomic,assign) id scaleCY; @end //Sprite category //Used to capture sprite cords and eval events @interface CCNode (events) -(void)addEventWithEvent:(NSString *)event callback:(void(^)(CCNode*node))back useDispatcher:(BOOL)disp; @end 

Este es el file CCNode + eventss.m (file principal)

 // // Created by Sebastian Winbladh on 2013-10-14. // Copyright (c) 2013 Sebastian Winbladh. All rights reserved. // #import "CCNode+events.h" @implementation EventLayer @synthesize callback,nodes; //Shanetworking instance +(id)shanetworkingEventLayer:(CCNode *)on callback:(void (^)(NSArray*nodeArray,NSSet*touches,NSString *event))block node:(NSArray *)addNode{ static dispatch_once_t onceToken; static EventLayer *eventLayer; dispatch_once(&onceToken, ^{ eventLayer = [[[EventLayer alloc]init]autorelease]; eventLayer.callback = block; [[eventLayer getParent:on] addChild:eventLayer]; }); [eventLayer.nodes addObject:addNode]; return eventLayer; } //Find top level parent child -(id)getParent:(CCNode*)on{ id ret=on; BOOL done=false; while(done == false){ ret = [ret parent]; if(![[ret parent] children]){ done = true; } }return ret; } -(void)callbackWithEvent:(NSString*)event nsSet:(NSSet *)set{ for(NSArray *lNodeArray in nodes){ self.callback(lNodeArray,set,event); } } -(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ [self callbackWithEvent:@"touchBegan" nsSet:touches]; } -(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{ [self callbackWithEvent:@"touchEnded" nsSet:touches]; } -(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{ [self callbackWithEvent:@"touchDrag" nsSet:touches]; } //Initilize -(id)init{ if(self = [super init]){ [self setTouchEnabled:YES]; nodes = [[NSMutableArray alloc]init]; } return self; } -(void)dealloc{ //Dealloc nodes [nodes release]; nodes = nil; [super dealloc]; } @end @implementation CCNode (props) @dynamic rotationCX,rotationCY,scaleCX,scaleCY; -(void)setRotationCX:(id)rotationCX{ objc_setAssociatedObject(self, @selector(rotationCX), rotationCX, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(id)rotationCX{return objc_getAssociatedObject(self, @selector(rotationCX));} -(void)setRotationCY:(id)rotationCY{ objc_setAssociatedObject(self, @selector(rotationCY), rotationCY, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(id)rotationCY{return objc_getAssociatedObject(self, @selector(rotationCY));} //Scales -(void)setScaleCX:(id)scaleCX{ objc_setAssociatedObject(self, @selector(scaleCX), scaleCX, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(id)scaleCX{return objc_getAssociatedObject(self, @selector(scaleCX));} -(void)setScaleCY:(id)scaleCY{ objc_setAssociatedObject(self, @selector(scaleCY), scaleCY, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(id)scaleCY{return objc_getAssociatedObject(self, @selector(scaleCY));} @end @implementation CCNode (events) -(void)createEventLayerWithEvent:(void(^)(NSArray*nodeArray,NSSet*touches,NSString *event))block node:(NSArray *)addNode{ [EventLayer shanetworkingEventLayer:self callback:block node:addNode]; } //Get top level child parent -(id)getParent:(CCNode*)on{ id ret=on; BOOL done=false; while(done == false){ ret = [ret parent]; if(![[ret parent] children]){ done = true; } }return ret; } //This function creates a custom bounding box. //It takes all childrens in the loop and calculate widths, hights, anchorPoints, positions, scales and rotations //to get the exact bounding box of the node. -(void)toggleRotationOnItems:(NSMutableArray *)items func:(NSString*)type{ for(NSArray *item in items){ CCNode *innerItems=[item objectAtIndex:0]; if([type isEqualToString:@"zero"]){ innerItems.rotationX=0; innerItems.rotationY=0; } if([type isEqualToString:@"reset"]){ innerItems.rotationX=((NSNumber*)innerItems.rotationCX).floatValue; innerItems.rotationY=((NSNumber*)innerItems.rotationCY ).floatValue; } } } -(CGPoint)getScalesOnChild:(CCNode *)item mother:(CCNode *)items{ CCNode *i=item; BOOL didFinish=false; CGPoint scales; scales.x = item.scaleX; scales.y = item.scaleY; while(didFinish == false){ if([i isEqual:items])didFinish=true; i = [i parent]; scales.x *= i.scaleX; scales.y *= i.scaleY; } return scales; } -(BOOL)isVisible:(CCNode*)node mother:(CCNode*)m{ CCNode *i=node; BOOL didFinish=false; while(didFinish == false){ if(i.visible == false){ return false; continue; } if([i isEqual:m])didFinish=true; i = [i parent]; } return true; } -(NSMutableArray*)createBoundingBox:(CCNode *)node{ node.rotationCX = [NSNumber numberWithFloat:node.rotationY ]; node.rotationCY = [NSNumber numberWithFloat:node.rotationY ]; node.scaleCX = [NSNumber numberWithFloat:node.scaleX ]; node.scaleCY = [NSNumber numberWithFloat:node.scaleY]; NSMutableArray *l=[[[NSMutableArray alloc]initWithObjects:node, nil]autorelease]; int c=1; NSMutableArray *ret=[[[NSMutableArray alloc]init]autorelease]; if(node.visible == true)ret=[[[NSMutableArray alloc]initWithObject:[NSArray arrayWithObjects:node,nil]]autorelease]; //This first loop will loop until the count var is stable// for(int r=0;r<c;r++){ //This loop will loop thru the child element list// for(int z=0;z<[[l objectAtIndex:r] children].count;z++){ //Push the element to the return array. CCNode *nodeItem = ((CCNode*)[[[l objectAtIndex:r] children] objectAtIndex:z]); nodeItem.rotationCX = [NSNumber numberWithFloat:nodeItem.rotationX ]; nodeItem.rotationCY = [NSNumber numberWithFloat:nodeItem.rotationY ]; nodeItem.scaleCX = [NSNumber numberWithFloat:nodeItem.scaleX ]; nodeItem.scaleCY = [NSNumber numberWithFloat:nodeItem.scaleY]; if([self isVisible:nodeItem mother:node])[ret addObject:[NSArray arrayWithObjects:nodeItem, nil]]; if([[[[[l objectAtIndex:r] children] objectAtIndex:z] children] objectAtIndex:0]){ [l addObject:[[[l objectAtIndex:r] children] objectAtIndex:z]]; c++; }//IF }//FOR }//FOR NSMutableArray *statickPoints = [[[NSMutableArray alloc]init]autorelease]; NSMutableArray *dynamicPoints = [[[NSMutableArray alloc]init]autorelease]; //Set the rotation to 0 so we can calculate the values better [self toggleRotationOnItems:ret func:@"zero"]; for(NSArray *items in ret){ //Create variables to hold the node point and the item it self CGPoint nodePoint; CCNode *innerItems=[items objectAtIndex:0]; //Check wich node world we will use nodePoint = [[innerItems parent] convertToWorldSpace:innerItems.position]; CGPoint scales=[self getScalesOnChild:innerItems mother:node]; float widthOffsetP1 = innerItems.contentSize.width*innerItems.anchorPoint.x*scales.x; float heightOffsetP1 = innerItems.contentSize.height*innerItems.anchorPoint.y*scales.y; float widthOffsetP1Flip = innerItems.contentSize.width*(1-innerItems.anchorPoint.x)*scales.x; float heightOffsetP1Flip = innerItems.contentSize.height*(1-innerItems.anchorPoint.y)*scales.y; //statick positions CGPoint point1 = CGPointMake(nodePoint.x-widthOffsetP1,nodePoint.y+heightOffsetP1Flip); CGPoint point2 = CGPointMake(nodePoint.x-widthOffsetP1+innerItems.contentSize.width*scales.x, nodePoint.y-heightOffsetP1+innerItems.contentSize.height*scales.y); CGPoint point3 = CGPointMake(nodePoint.x-widthOffsetP1+innerItems.contentSize.width*scales.x, nodePoint.y-heightOffsetP1); CGPoint point4 = CGPointMake(nodePoint.x-widthOffsetP1,nodePoint.y-heightOffsetP1); //Append to array [statickPoints addObject:[NSArray arrayWithObjects:innerItems, [NSValue valueWithCGPoint:point1], [NSValue valueWithCGPoint:point2], [NSValue valueWithCGPoint:point3], [NSValue valueWithCGPoint:point4],nil]]; } //Callculate mother and child rotations for(NSArray *items in statickPoints){ NSValue *point1 = [items objectAtIndex:1]; NSValue *point2 = [items objectAtIndex:2]; NSValue *point3 = [items objectAtIndex:3]; NSValue *point4 = [items objectAtIndex:4]; int matrix_length=3; CGPoint points[matrix_length]; points[0] = [point1 CGPointValue]; points[1] = [point2 CGPointValue]; points[2] = [point3 CGPointValue]; points[3] = [point4 CGPointValue]; // Seting the statick positions to the rotations for(int i=0;i<=matrix_length;i++){ CGPoint nodePoint; CCNode *item = [items objectAtIndex:0]; BOOL didFinish = false; while(didFinish == false){ nodePoint = [[item parent] convertToWorldSpace:item.position]; float widthOffsetP1 = (points[i].x - (nodePoint.x)); float heightOffsetP1 = (points[i].y - (nodePoint.y)); float radians1=sqrt(fabs(powf(widthOffsetP1, 2))+fabs(powf(heightOffsetP1,2))); float newRotation1 =CC_RADIANS_TO_DEGREES(atan2(widthOffsetP1,heightOffsetP1)) + ((NSNumber*)item.rotationCX).floatValue ; float p1RotApplyed=(radians1) * sinf(CC_DEGREES_TO_RADIANS(newRotation1)); float p2RotApplyed=(radians1) * cosf(CC_DEGREES_TO_RADIANS(newRotation1)); points[i].x-=-p1RotApplyed+(widthOffsetP1); points[i].y-=-p2RotApplyed+(heightOffsetP1); if([item isEqual:node]){ didFinish=true; } item = [item parent]; } } [dynamicPoints addObject:[NSArray arrayWithObjects:[NSValue valueWithCGPoint:points[0]], [NSValue valueWithCGPoint:points[1]], [NSValue valueWithCGPoint:points[2]], [NSValue valueWithCGPoint:points[3]], nil]]; /* CCLabelTTF *la=[CCLabelTTF labelWithString:@"O" fontName:@"Arial" fontSize:6]; la.anchorPoint=ccp(0.5,0.5); la.position=points[3]; [[self getParent:node ]addChild:la]; CCLabelTTF *la1=[CCLabelTTF labelWithString:@"O" fontName:@"Arial" fontSize:6]; la1.anchorPoint=ccp(0.5,0.5); la1.position=points[2]; [[self getParent:node ]addChild:la1]; CCLabelTTF *la2=[CCLabelTTF labelWithString:@"O" fontName:@"Arial" fontSize:6]; la2.anchorPoint=ccp(0.5,0.5); la2.position=points[1]; [[self getParent:node ]addChild:la2]; CCLabelTTF *la3=[CCLabelTTF labelWithString:@"O" fontName:@"Arial" fontSize:6]; la3.anchorPoint=ccp(0.5,0.5); la3.position=points[0]; [[self getParent:node ]addChild:la3];*/ } //Reset rotations [self toggleRotationOnItems:ret func:@"reset"]; return dynamicPoints; } -(BOOL)boxContainsPoint:(CGPoint)p box:(NSMutableArray*)a test:(CCNode*)t{ BOOL returns=false; NSMutableArray *ret=[[[NSMutableArray alloc]init]autorelease]; for(NSArray *items in a){ NSValue *point1 = [items objectAtIndex:0]; NSValue *point2 = [items objectAtIndex:1]; NSValue *point3 = [items objectAtIndex:2]; NSValue *point4 = [items objectAtIndex:3]; int matrix_length=4; CGPoint points[matrix_length*2+1]; points[8] = points[4] = points[0] = [point1 CGPointValue]; points[5] = points[1] = [point2 CGPointValue]; points[6] = points[2] = [point3 CGPointValue]; points[7] = points[3] = [point4 CGPointValue]; NSMutableArray *hits=[[[NSMutableArray alloc]init]autorelease]; int p1=0; float max=0; for(int i=0;i<=matrix_length;i++){if(points[i].y>=max)p1=i;max=points[i].y;} for(int i=0;i<matrix_length;i+=2){ CGPoint graphOrigo = ccp(points[p1+i+1].x,points[p1+i].y); double x = (graphOrigo.xp.x); double k = (graphOrigo.y-points[p1+i+1].y)/(graphOrigo.x-points[p1+i].x); double m = (graphOrigo.y-points[p1+i+1].y); double y = (-k*x+m); if((graphOrigo.yp.y)>(y) && i <=1){ [hits addObject:[NSNumber numberWithBool:YES]]; }else if((graphOrigo.yp.y)<(y) && i >=1){ [hits addObject:[NSNumber numberWithBool:YES]]; }else{ [hits addObject:[NSNumber numberWithBool:NO]]; } graphOrigo = ccp(points[p1+i+1].x,points[p1+i+2].y); y = (graphOrigo.yp.y); k = (graphOrigo.x-points[p1+i+2].x)/(graphOrigo.y-points[p1+i+1].y); m = (graphOrigo.x-points[p1+i+2].x); x = (-k*y+m); if((graphOrigo.xp.x)>(x) && i <=1){ [hits addObject:[NSNumber numberWithBool:YES]]; }else if((graphOrigo.xp.x)<(x) && i >=1){ [hits addObject:[NSNumber numberWithBool:YES]]; }else{ [hits addObject:[NSNumber numberWithBool:NO]]; } } BOOL hit=YES; for(NSNumber *bools in hits){ if(bools.boolValue == NO){ hit=NO; } } [ret addObject:[NSNumber numberWithBool:hit]]; } for(NSNumber *b in ret){ if(b.boolValue == YES){ returns=true; } } return returns; } -(BOOL)validateToush:(NSSet *)touches nodePoint:(CCNode *)node{ UITouch *touch = [touches anyObject]; id parent = [self getParent:self]; //Touch to global node space CGPoint touchPoint = [parent convertTouchToNodeSpace:touch]; NSMutableArray *nodeBox = [self createBoundingBox:(CCNode *)node]; //Validating of hit point if([self boxContainsPoint:touchPoint box:nodeBox test:node])return true; return false; } -(void)addEventWithEvent:(NSString *)event callback:(void (^)(CCNode*node))back useDispatcher:(BOOL)disp{ //Add a cc layer so we can capture toushes [self createEventLayerWithEvent:^(NSArray*nodeArray,NSSet*touches,NSString *event) { //Calback block NSArray *lNodeArray=nodeArray; CCNode *lNode = [lNodeArray objectAtIndex:0]; void(^nodeBack)(CCNode*node) =[nodeArray objectAtIndex:2]; BOOL disp =((NSNumber *)[nodeArray objectAtIndex:3]).boolValue; if([[lNodeArray objectAtIndex:1] isEqualToString:@"touchBegan"]){ //Return to callback block if([event isEqualToString:@"touchBegan"] && [lNode validateToush:touches nodePoint:lNode] || disp==NO && [event isEqualToString:@"touchBegan"])nodeBack((CCNode*)[nodeArray objectAtIndex:0]); }else if([[lNodeArray objectAtIndex:1] isEqualToString:@"touchEnded"]){ //Return to callback block if([event isEqualToString:@"touchEnded"] && [lNode validateToush:touches nodePoint:lNode] || disp==NO && [event isEqualToString:@"touchEnded"])nodeBack((CCNode*)[nodeArray objectAtIndex:0]); }else if([[lNodeArray objectAtIndex:1]isEqualToString:@"touchDrag"]){ //Return to callback block if([event isEqualToString:@"touchDrag"] && [lNode validateToush:touches nodePoint:lNode] || disp==NO && [event isEqualToString:@"touchDrag"])nodeBack((CCNode*)[nodeArray objectAtIndex:0]); } } node:[NSArray arrayWithObjects:self,event,Block_copy(back),[NSNumber numberWithBool:disp], nil]]; } @end 

Usar este detector de events es muy simple

  1. Incluya el file CCSprite + eventss.h en su proyecto.
  2. Cree un CCNode / CCSprite (yourNode) en el que desea agregar un eventlistener.
  3. Entonces, crea el evento codificando esto

    [yourNode addEventWithEvent: @ "toqueBegan" callback: ^ (CCNode * nodo) {

    NSLog (@ "Touch comenzó en el nodo");

    } useDispatcher: SÍ];

El parámetro addEventWithEvent toma tres types

  • touchBegan = disparado cuando tu dedo toca el nodo
  • touchEnded = disparó cuando su dedo libera el nodo
  • touchDrag == disparado al mover el dedo sobre el nodo

La callback toma un bloque de callback que se activará en el evento anterior.

El useDispatcher toma un valor BOOL (SÍ o NO). Si se establece en SÍ, el evento disparará en el CCNode. Si se establece en NO, el evento se disparará en la pantalla.