xcode 在 CCSprite 上检测触摸
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7883670/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detect Touch on CCSprite
提问by Will Coughlin
I'm new to cocos2d so excuse my ignorance, but I would like to know how to detect when a sprite has been touched and call a method when it has been touched.
我是 cocos2d 的新手,所以请原谅我的无知,但我想知道如何检测精灵何时被触摸并在它被触摸时调用一个方法。
I've defined and added my sprite like this:
我已经像这样定义并添加了我的精灵:
CCSprite *infoButton = [CCSprite spriteWithFile: @"info.png"];
[infoButton setPosition:CGPointMake(450, 290)];
[menuBG addChild:infoButton];
I've followed various resources but they have been very vague, most of which the sprite was set in its own class.
我遵循了各种资源,但它们非常模糊,其中大部分精灵都设置在自己的类中。
Thanks in advance.
提前致谢。
回答by LearnCocos2D
In regular Cocos2D:
在常规 Cocos2D 中:
-(void) ccTouchesBegan:(NSSet*)touches withEvent:(id)event
{
CCDirector* director = [CCDirector sharedDirector];
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 ...
}
}
To demonstrate how much Kobold2Dsimplifies this over Cocos2D's approach:
为了演示Kobold2D比 Cocos2D 的方法简化了多少:
-(void) update:(ccTime)delta
{
KKInput* input = [KKInput sharedInput];
if ([input isAnyTouchOnNode:infoButton touchPhase:KKTouchPhaseBegan])
{
// code for when user touched infoButton sprite goes here ...
}
}
回答by xuanweng
Why dont you use CCMenuItemImage?
为什么不使用 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];
and another function whenever the user pressed the button..
以及每当用户按下按钮时的另一个功能..
-(void)pressed:(id)sender
{
// whatever you would like to do here...
}
回答by brigadir
The solution depends on your code architecture. For menu items use xuanwengvariant. Alternatively you may check intersection of touch point with sprite bounds in ccTouchBegan
method of parent layer. You need to transform touch point to layer space (in common case this transform is identity) and check CGRectContainsPoint ([sprite boundingBox], touchPos)
解决方案取决于您的代码架构。对于菜单项,使用xuanweng变体。或者,您可以在ccTouchBegan
父层的方法中检查触摸点与精灵边界的交集。您需要将接触点转换为图层空间(通常情况下,此转换是身份)并检查CGRectContainsPoint ([sprite boundingBox], touchPos)
回答by user1220713
I made this custom event listener a while ago
不久前我做了这个自定义事件监听器
This is the CCNode+events.h file(the header file)
这是CCNode+events.h文件(头文件)
//
// 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)sharedEventLayer:(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
This is the CCNode+events.m file(Main file)
这是 CCNode+events.m 文件(主文件)
//
// 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;
//Shared instance
+(id)sharedEventLayer:(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 sharedEventLayer: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.x-p.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.y-p.y)>(y) && i <=1){
[hits addObject:[NSNumber numberWithBool:YES]];
}else if((graphOrigo.y-p.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.y-p.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.x-p.x)>(x) && i <=1){
[hits addObject:[NSNumber numberWithBool:YES]];
}else if((graphOrigo.x-p.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
To use this event listener is very simple
使用这个事件监听器非常简单
- Include the CCSprite+events.h file in your project.
- Create a CCNode/CCSprite(yourNode) that you like to add an eventlistener on.
Then your create the event by coding this
[yourNode addEventWithEvent:@"touchBegan" callback:^(CCNode *node) {
NSLog(@"Touch began on node");
} useDispatcher:YES];
- 在您的项目中包含 CCSprite+events.h 文件。
- 创建一个您想添加事件侦听器的 CCNode/CCSprite(yourNode)。
然后你通过编码来创建事件
[yourNode addEventWithEvent:@"touchBegan" 回调:^(CCNode *node) {
NSLog(@"触摸开始于节点");
} useDispatcher:YES];
The addEventWithEvent parameter takes three types
addEventWithEvent 参数采用三种类型
- touchBegan = fired when your finger touches the node
- touchEnded = fired when your finger releases the node
- touchDrag == fired when moving you finger on the node
- touchBegan = 当您的手指触摸节点时触发
- touchEnded = 当你的手指松开节点时触发
- 当您在节点上移动手指时触发 touchDrag ==
The callback takes a callback block that will be fired on the event above.
回调采用将在上述事件上触发的回调块。
The useDispatcher takes a BOOL value(YES or NO). If it get′s set to YES the event will fire on the CCNode. If it get′s set to NO the event will fire on the screen
useDispatcher 接受一个 BOOL 值(YES 或 NO)。如果设置为 YES,则事件将在 CCNode 上触发。如果设置为 NO,则事件将在屏幕上触发