Sprite Kit - cant get custom class data from object - ios

I am trying to get the value from my custom class SKNode object. The problem is when I touch on the object no matter what is gives me the same value regardless of the object I touch on.
(blue button) and I cannot get the buttonID,buttonType that I set earlier.
Everything works well, except when I need to get the buttonType, buttonID of the object I touch or drag over.
I am not sure where I am going wrong, any help or push in the right direction would be great. Thanks.
here is my custom class .h file.
#import <SpriteKit/SpriteKit.h>
#interface ButtonNode : SKNode {
SKNode *buttonCustom;
}
#property int buttonType, buttonColumn, buttonID, xPos, yPos;
#property NSString *buttonName;
-(id)initWithButtonType:(int)buttonType;
#end
Here is my custom class .m file
#import "ButtonNode.h"
#define kBoxSize CGSizeMake(40, 40)
#implementation ButtonNode
#synthesize buttonColumn,buttonType,buttonID,xPos,yPos,buttonName;
static const uint32_t blueCategory = 1 << 0;
static const uint32_t redCategory = 1 << 1;
static const uint32_t greenCategory = 1 << 2;
static const uint32_t yellowCategory = 1 << 3;
-(id)initWithButtonType:(int)buttonType {
self = [super init];
if (buttonType == 1) {
NSLog(#"BLUE BUTTON CREATE");
[self addButtonBlue];
}
if (buttonType == 2) {
NSLog(#"RED BUTTON CREATE");
[self addButtonRed];
}
if (buttonType == 3) {
NSLog(#"Green BUTTON CREATE");
[self addButtonGreen];
}
if (buttonType == 4) {
NSLog(#"Yellow BUTTON CREATE");
[self addButtonYellow];
}
return self;
}
- (void) addButtonBlue {
SKSpriteNode *rect;
//button type 1
rect = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"1";
rect.physicsBody.categoryBitMask = blueCategory;
rect.physicsBody.contactTestBitMask = blueCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonRed {
SKSpriteNode *rect;
//button type 2
rect = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"2";
rect.physicsBody.categoryBitMask = redCategory;
rect.physicsBody.contactTestBitMask = redCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonGreen {
SKSpriteNode *rect;
//button type 3
rect = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"3";
rect.physicsBody.categoryBitMask = greenCategory;
rect.physicsBody.contactTestBitMask = greenCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonYellow {
SKSpriteNode *rect;
//button type 4
rect = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"4";
rect.physicsBody.mass = 1;
rect.physicsBody.categoryBitMask = yellowCategory;
rect.physicsBody.contactTestBitMask = yellowCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
#end
Here is where I create the buttons.
(at top of file with rest of global ivar )
ButtonNode * newButton;
for (int i = 1; i <= 6; i++) {
//create random Int
int tmpInt = arc4random() %3;
NSLog(#"tmp %i" ,tmpInt);
column1.position = CGPointMake(100, self.frame.size.height - 40);
if (tmpInt == 0) {
//button type 1
newButton = [[ButtonNode alloc] initWithButtonType:1];
newButton.xPos = column1.position.x;
newButton.yPos = column1.position.y *i;
newButton.buttonID = 344224351; //unique name
newButton.buttonColumn = 2;
newButton.buttonType = 1;
[column1 addChild:newButton];
blueTotal++;
totalButtons++;
column1Total++;
}
if (tmpInt == 1) {
//button type 2
newButton = [[ButtonNode alloc] initWithButtonType:2];
newButton.xPos = column1.position.x;
newButton.yPos = column1.position.y *i;
newButton.buttonID = 344224351; //unique name
newButton.buttonColumn = 2;
newButton.buttonType = 1;
[column1 addChild:newButton];
redTotal++;
totalButtons++;
column1Total++;
}
}
Here is the Part that is not working correctly.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:loc];
for (SKNode *nod in nodes) {
NSString *tmp = nod.name;
if (tmp.length !=0) {
NSString * tmpType = nod.name;
if ([tmpType isEqualToString:#"1"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"1";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"2"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"2";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"3"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"3";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"4"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"4";
NSLog (#"%d",newButton.buttonType);
}
}
}
}
}

A SKNode does not have those properties.
Try this just inside your for loop :
ButtonNode *myButton = (ButtonNode *)nod;
That will cast nod correctly as a ButtonNode, and you can use myButton like this :
NSLog(#"%d",myButton.buttonType);
Then you should be able to access the properties you have defined in the ButtonNode class.
You might only want to do that cast if you are sure it's a ButtonNode, but was just trying to help you understand why those properties would NEVER be accessible given your current code.
Also, your usage of newButton in that loop in touchesBegan is not what I think you 'think' it is. It's going to be the last button created, not the current node being stored in nod in the loop.

Related

Move Buttons to image targets

I am new in objective c and now i try to build game like What's the word and i have some problems with targets coordinates and button passing into targets. I create a -(void)dealRandomWord here I am take randome a word from plist and output all my subview on screen. I call it from viewDidLoad: [self dealRandomWord].
And here is my problem:
#pragma mark Targets
_targets = [NSMutableArray arrayWithCapacity:word1len];
for (NSInteger i = 0; i < word1len; i++) {
NSString *letter = [word1 substringWithRange:NSMakeRange(i, 1)];
TargetView *target = [[TargetView alloc] initWithLetter:letter andSideLength: letterSide];
target.center = CGPointMake(xCenterTarget + i * (letterSide + kLetterMargin), placesView.frame.size.height / 2);
[placesView addSubview:target];
NSLog(#"biiitch %#", NSStringFromCGPoint(target.center));
}
In target.center i have the coordinates of targets where i want to move my buttons by click. When i click one of the button it moved but not in the target, it happens because in
-(void)placeForButton:(InputButtonsView*)inputButtonsView atTarget:(TargetView*)targetView {
targetView.center coordinate equal {0,0}
How can i pass coordinates for each of target to my button that it's move to target. Every next button should move to next target.
Here is image: Screenshot
Thanks for any help!!!
Here is my code:
-(void)dealRandomWord {
#pragma mark Level and words init
NSAssert(self.level.words, #"Level not loaded");
// random word from plist
NSInteger randomIndex = arc4random()%[self.level.words count];
NSArray* anaPair = self.level.words[ randomIndex ];
NSString* question = anaPair[0]; // question
NSString* word1 = anaPair[1]; // answer
NSString* word2 = anaPair[2]; // some letters
NSString* helpstr = anaPair[3]; // helper
NSLog(#"qweqweq %# %# %#" , word1 , word2 , helpstr);
NSInteger word1len = [word1 length];
NSInteger word2len = [word2 length];
NSLog(#"phrase1[%li]: %#", (long)word1len, word1);
NSLog(#"phrase2[%li]: %#", (long)word2len, word2);
NSLog(#"question %#", question);
float letterSide = ceilf (kScreenWidth * 0.9 / (float)MAX(word1len, word2len) - kLetterMargin);
float xOffset = (kScreenWidth - (float)MAX(word1len, word2len) * (letterSide + kLetterMargin))/3;
xOffset += letterSide/2;
float yOffset = 1.5* letterSide;
#pragma mark QuestionView init
QuestionView *quv = [[QuestionView alloc] init];
quv.questionLabel.text = question;
[self.view addSubview:quv];
quv.center = CGPointMake(185, 210);
#pragma mark PlacesView init
PlacesView *placesView = [[PlacesView alloc] init];
[self.view addSubview:placesView];
placesView.center = CGPointMake(185, 400);
//Center x position for targets
float xCenterTarget = ((placesView.frame.size.width / 2) - ((word1len / 2) * letterSide ));
#pragma mark LetterView init
LettersView *lettersView = [[LettersView alloc] init];
[self.view addSubview:lettersView];
lettersView.center = CGPointMake(185, 500);
#pragma mark Targets
_targets = [NSMutableArray arrayWithCapacity:word1len];
for (NSInteger i = 0; i < word1len; i++) {
NSString *letter = [word1 substringWithRange:NSMakeRange(i, 1)];
TargetView *target = [[TargetView alloc] initWithLetter:letter andSideLength: letterSide];
target.center = CGPointMake(xCenterTarget + i * (letterSide + kLetterMargin), placesView.frame.size.height / 2);
[placesView addSubview:target];
NSLog(#"coord target init %#", NSStringFromCGPoint(target.center));
}
#pragma mark LettersView init
//init letters list
_letters = [NSMutableArray arrayWithCapacity: word2len];
//create letter
for (NSInteger i=0;i<word2len;i++) {
NSString* letter = [word2 substringWithRange:NSMakeRange(i, 1)];
if (![letter isEqualToString:#" "]) {
InputButtonsView *buttons = [[InputButtonsView alloc] initWithLetter:letter andSideLength:letterSide];
buttons.center = CGPointMake(xOffset + i * (letterSide + kLetterMargin), lettersView.frame.size.height /2); // "/3*4" kScreenHeight/4*3
if (i > 6) {
buttons.center = CGPointMake(- 7 * xOffset + i * (letterSide + kLetterMargin), lettersView.frame.size.height/2 + (letterSide + kLetterMargin)); // "/3*4"
}
buttons.clickDelegate = self;
[lettersView addSubview:buttons];
//[buttons addSubview:buttons];
[_letters addObject: letter];
}
}
}
-(void)inputButtonView:(InputButtonsView *)inputButtonView didPress:(CGPoint)didPress {
TargetView *targetView = nil;
NSLog(#"did press x = %f, y = %f", didPress.x , didPress.y);
for(TargetView *tv in _targets) {
if(CGRectContainsPoint(tv.frame, didPress)){
targetView = tv;
break;
}
}
[self placeForButton:inputButtonView atTarget:targetView];
if (targetView != nil) {
NSLog(#"Kek");
if ([targetView.letter isEqualToString: inputButtonView.letter]) {
[self placeForButton:inputButtonView atTarget:targetView];
}
}
}
-(void)placeForButton:(InputButtonsView*)inputButtonsView atTarget:(TargetView*)targetView {
targetView.isMatched = YES;
inputButtonsView.isMatched = YES;
inputButtonsView.userInteractionEnabled = NO;
CGPoint originButtons = [self.view.superview convertPoint:CGPointZero fromView:inputButtonsView];
CGPoint originTargets = [self.view.superview convertPoint:CGPointZero fromView:targetView];
inputButtonsView.center = originButtons;
targetView.center = originTargets;
NSLog(#"OriginButtons = %# , OriginTargets = %#", NSStringFromCGPoint(originButtons) , NSStringFromCGPoint(originTargets));
inputButtonsView.center = targetView.center;
NSLog(#"TARGETVIEW.center %#", NSStringFromCGPoint(targetView.center));
}

NodesAtPoint does not find a node

I'm trying to create 10 balloons in my app. I create a random CGPoint to set my node's position and check, in case that already exist a node at this point I try again.
I don't know why, the balloons keep being placed over another balloon.
Any ideia of what I am doing wrong?
Here is my code:
-(void)gerarBaloes:(int)quantidade
{
balloons = [NSMutableArray new];
u_int32_t maxHorizontal = 900;
u_int32_t minHorizontal = 100;
u_int32_t maxVertical = 650;
u_int32_t minVertical = 100;
for (int i=1; i<= quantidade; i++) {
CGPoint posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
while (![self validPosition:posicao]) {
posicao = CGPointMake(arc4random_uniform(maxHorizontal - minHorizontal + 1) + minHorizontal, arc4random_uniform(maxVertical - minVertical + 1) + minVertical);
}
balao* nBalao = [[balao alloc]initWithPosition:posicao];
SKLabelNode* numero = [[SKLabelNode alloc]initWithFontNamed:#"Arial Rounded MT Bold"];
numero.text = [NSString stringWithFormat:#"%d",i];
numero.zPosition = 1;
nBalao.body.zPosition = 0;
[nBalao addChild:numero];
nBalao.body.name = #"balloon";
[balloons addObject:nBalao];
[self addChild:nBalao];
}
}
And this is my validation method:
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self nodesAtPoint:position];
NSLog(#"total de nodes %d",nodes.count);
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
return NO;
}
}
return YES;
}else{
return YES;
}
}
Balao.m class:
#implementation balao
-(id)initWithPosition:(CGPoint)pos
{
if (self = [super init]) {
self.position = pos;
int n = arc4random_uniform(4);
_balloonAtlas = [SKTextureAtlas atlasNamed:[NSString stringWithFormat:#"balloon%d",n]];
_explosionFrames = [NSMutableArray array];
int numImages = _balloonAtlas.textureNames.count;
for (int i=1; i<= numImages; i++){
NSString *textureName = [NSString stringWithFormat:#"balloon_%d",i];
SKTexture *temp = [_balloonAtlas textureNamed:textureName];
[_explosionFrames addObject:temp];
}
SKTexture *textInicial = [_explosionFrames[0] copy];
[_explosionFrames removeObjectAtIndex:0];
self.body = [SKSpriteNode spriteNodeWithTexture:textInicial];
[self addChild:_body];
}
return self;
}
- (void)explodeBalloon
{
SKAction* explosao = [SKAction animateWithTextures:_explosionFrames timePerFrame:0.02f resize:NO restore:YES];
[self.body runAction:explosao completion:^(void){
[self removeFromParent];
}];
[self runAction:[SKAction playSoundFileNamed:#"popSound.mp3" waitForCompletion:NO]];
}
This is how the balloons appears:
Edit:
I tried the suggested method, with CGRectCointainsPoint, but it didn't work too. :/
Checking for nodes at a given point is not enough to prevent overlap. You need to check whether the point falls inside the frame of another balloon. You can modify the function like this
-(BOOL)validPosition:(CGPoint)position
{
NSArray* nodes = [self children];
if (nodes.count > 0) {
for (SKNode* n in nodes) {
if ([n isKindOfClass:[balao class]]) {
if (CGRectContainsPoint([n getBalloonFrame], position)) // Changed line
{
return NO
}
}
}
return YES;
}
return YES;
}
CGRectContainsPoint checks whether a given rectangle contains a point.
Inside balao class define the following function. Also note the changed line in the above code.
-(void)getBalloonFrame
{
return CGRectOffset(self.body.frame, self.frame.origin.x, self.frame.origin.y)
}

Can't figure out these errors:

I'm working on a game and I started getting this after I added a menu on the story board, even after deleting it, I get this error:
2014-09-05 19:55:22.155 hygvhgvbv[2875:60b] Cannot find executable for CFBundle 0x998cb40 </Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/System/Library/AccessibilityBundles/CertUIFramework.axbundle> (not loaded)
hygvhgvbv(2875,0x20581a8) malloc:
*** mach_vm_map(size=8388608) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
libc++abi.dylib: terminating with uncaught exception of type std::bad_alloc: std::bad_alloc
Here's my code:
Myscene.h
#import <SpriteKit/SpriteKit.h>
#interface MyScene : SKScene
#property (strong, nonatomic) SKLabelNode *scoreLabel;
#property (nonatomic) NSInteger score;
#property (nonatomic) BOOL gameOver;
#property SKEmitterNode *shatter;
typedef NS_ENUM(NSInteger, SpriteType) {
SpriteTypeBackground,
SpriteTypeSquare
};
Myscene.m
#import "MyScene.h"
#interface MyScene()
#property SKSpriteNode *background;
#property SKSpriteNode *square;
#end
#implementation MyScene
#define kNumberOfColors 2 //Setting total number of possible colors
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
[self runAction:[SKAction repeatActionForever: [SKAction sequence:#[[SKAction performSelector:#selector(addSquareAndBackground) onTarget:self] ]]]];
//Score Label
float margin = 10;
self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
self.scoreLabel.text = #"Score: 0";
self.scoreLabel.fontSize = [self convertFontSize:14];
self.scoreLabel.zPosition = 4;
self.scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
self.scoreLabel.position = CGPointMake(margin, margin);
[self addChild:self.scoreLabel];
}
return self;
}
- (float)convertFontSize:(float)fontSize
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return fontSize * 2;
} else {
return fontSize;
}
}
-(void)addSquareAndBackground {
_background = [self createSpriteWithType:SpriteTypeBackground];
_square = [self createSpriteWithType:SpriteTypeSquare];
}
-(void)removeSquareAndBackground {
[_background removeFromParent];
[_square removeFromParent];
}
-(SKSpriteNode *) createSpriteWithType:(SpriteType)type {
// Select a color randomly
NSString *colorName = [self randomColorName];
SKSpriteNode *sprite;
if (type == SpriteTypeBackground) {
NSString *name = [NSString stringWithFormat:#"%#Outline",colorName];
sprite = [SKSpriteNode spriteNodeWithImageNamed:name];
sprite.name = name;
sprite.size = CGSizeMake(CGRectGetHeight(self.frame), CGRectGetWidth(self.frame));
}
else {
sprite = [SKSpriteNode spriteNodeWithImageNamed:colorName];
sprite.name = [NSString stringWithFormat:#"%#Sprite",colorName];
sprite.size = CGSizeMake(50, 50);
}
sprite.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
[self addChild:sprite];
return sprite;
}
- (NSString *) randomColorName {
NSString *colorName;
switch (arc4random_uniform(kNumberOfColors)) {
case 0:
colorName = #"blue";
break;
case 1:
colorName = #"pink";
break;
// Add more colors here
default:
break;
}
return colorName;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
NSString *pinkShatterPath = [[NSBundle mainBundle] pathForResource:#"pinkShatter" ofType:#"sks"];
SKEmitterNode *pinkShatter = [NSKeyedUnarchiver unarchiveObjectWithFile:pinkShatterPath];
pinkShatter.particlePosition = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
if (node == _square) {
// Extract the color name from the node name
NSArray *squareNameParts = [node.name componentsSeparatedByCharactersInSet:[NSCharacterSet uppercaseLetterCharacterSet]];
// Extract the color name from the node name
NSArray *backgroundNameParts = [_background.name componentsSeparatedByCharactersInSet:[NSCharacterSet uppercaseLetterCharacterSet]];
// Compare if the colors match
if ([backgroundNameParts[0] isEqualToString: squareNameParts[0]]) {
//Add SKAction to add 1 to score label
[SKAction performSelector:#selector(removeSquareAndBackground) onTarget:self];
[self addChild:pinkShatter];
self.score += 10;
//NSLog(#"Score"); for console error detection
} else {
//NSLog(#"Lost"); //Add SKAction to display game-over menu
SKLabelNode *gameOverLabel = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
gameOverLabel.text = #"You Lose!!!!!";
gameOverLabel.fontSize = 20;
gameOverLabel.zPosition = 4;
gameOverLabel.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[gameOverLabel setScale:0.1];
[self addChild:gameOverLabel];
[gameOverLabel runAction:[SKAction scaleTo:1.0 duration:0.5]];
self.gameOver = YES;
return;
}
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
[self.scoreLabel setText:[NSString stringWithFormat:#"Score: %ld", (long)self.score]];
}
#end
I can't figure out how to fix these errors.

Set Time Clicking on Dial

I am working on an alarm app in which I have to show an analogue clock. The functionality is to set time by clicking on the circumference of the circle displayed as the clock dial.
I have succesfully done this too. But the issue comes at 6 O'Clock or 30 min. The angle comes out to be nan for that which makes my smooth clock totally abrupt.
Here is the code.
myPath1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(5, 5, 244, 244)];
[myPath1 closePath];
[myPath1 setLineWidth:20];
// Draw a shape Layer with Triangle Path
CAShapeLayer *shapeLayer1 = [CAShapeLayer layer];
shapeLayer1.fillColor = [UIColor clearColor].CGColor;
shapeLayer1.strokeColor = [UIColor clearColor].CGColor;
shapeLayer1.lineWidth = 20;
shapeLayer1.path = myPath1.CGPath;
// Add it to your view
[circleView.layer addSublayer:shapeLayer1];
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:circleView];
if([self containsPointOnStroke:currentPoint onPath:myPath1])
{
[self calculateAngle:currentPoint];
}
}
- (BOOL)containsPointOnStroke:(CGPoint)point onPath:(UIBezierPath *)path
{
CGPathRef cgPath = path.CGPath;
CGPathRef strokedPath = CGPathCreateCopyByStrokingPath(cgPath, NULL, 10,
kCGLineCapRound, kCGLineJoinRound, 1);
BOOL pointIsNearPath = CGPathContainsPoint(strokedPath, NULL, point, NO);
CGPathRelease(strokedPath);
return pointIsNearPath;
}
//---- calculate angle from the point of touch taking center as the reference ----//
- (void) calculateAngle : (CGPoint)currentPoint
{
double distance = sqrt(pow((currentPoint.x - 127), 2.0) + pow((currentPoint.y - 5), 2.0));
float val = ((2 * 14884) - powf((distance), 2)) / (2 * 14884);
float angleVal = acosf(val) * 180/M_PI;
float timeval = angleVal;
//NSLog(#"angleVal : %f timeVal : %.2f", angleVal, timeval);
if (isnan(angleVal))
{
// if the value of the angle val is infinite
if(check == 0)
[hourBtn setTitle:#"06" forState:UIControlStateNormal];
else
[minBtn setTitle:#"30" forState:UIControlStateNormal];
}
else
{
if(check == 0)
{
if (currentPoint.x >= 127)
[hourBtn setTitle:[self getHour:timeval reverseTIme:NO] forState:UIControlStateNormal];
else
[hourBtn setTitle:[self getHour:timeval reverseTIme:YES] forState:UIControlStateNormal];
}
else
{
if (currentPoint.x >= 127)
[minBtn setTitle:[self getMin:timeval reverseTIme:NO] forState:UIControlStateNormal];
else
[minBtn setTitle:[self getMin:timeval reverseTIme:YES] forState:UIControlStateNormal];
}
}
[timeLbl setText:[NSString stringWithFormat:#"%02d:%02d",[hourBtn.titleLabel.text intValue], [minBtn.titleLabel.text intValue]]];
}
//---- get the hour time from the angle ----//
- (NSString *) getHour :(float) angle reverseTIme:(BOOL) reverseCheck
{
int minuteCount = angle * 2;
int quo = minuteCount / 60;
int temp = quo;
if(quo == 0)
quo = 12;
NSString *timeStr = [NSString stringWithFormat:#"%02d", quo];
quo = temp;
if (reverseCheck)
{
int revQuo = 11 - quo;
if(revQuo == 0)
revQuo = 12;
timeStr = [NSString stringWithFormat:#"%02d", revQuo];
}
return timeStr;
}
// get the min time from the angle
- (NSString *) getMin :(float) angle reverseTIme:(BOOL) reverseCheck
{
int minuteCount = angle;
int rem = minuteCount / 6;
NSString *timeStr = [NSString stringWithFormat:#"%02d", rem];
if (reverseCheck)
{
int revRem = 60 - rem;
if(revRem == 60)
revRem = 0;
timeStr = [NSString stringWithFormat:#"%02d", revRem];
}
//NSLog(#"timeStr : %#", timeStr);
return timeStr;
}

Detect Touch on CCSprite

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.
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.
In regular 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 Kobold2D simplifies this over Cocos2D's approach:
-(void) update:(ccTime)delta
{
KKInput* input = [KKInput sharedInput];
if ([input isAnyTouchOnNode:infoButton touchPhase:KKTouchPhaseBegan])
{
// code for when user touched infoButton sprite goes here ...
}
}
Why dont you use 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...
}
The solution depends on your code architecture. For menu items use xuanweng variant. 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)
I made this custom event listener a while ago
This is the CCNode+events.h file(the header file)
//
// 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)
//
// 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];
The addEventWithEvent parameter takes three types
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
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

Resources