2

I'm using this code to detect and see if the users tap was inside the frame of my SKSpriteNode, and if it is, remove the node from the screen. But I only want the node that was tapped to disappear.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */

    for (UITouch *touch in touches) {
            CGPoint location = [touch locationInNode:self];

        if ((location.x > self.crate.frame.origin.x && location.x < self.crate.frame.origin.x + self.crate.frame.size.width) &&
            (location.y > self.crate.frame.origin.y && location.y < self.crate.frame.origin.y + self.crate.frame.size.height)) {

            [self.crate removeFromParent];
        }
    }
}

In my update method, I am calling a method, addCrate: to spawn the node every second.

- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {

    self.lastSpawnTimeInterval += timeSinceLast;
    if (self.lastSpawnTimeInterval > 1) {
        self.lastSpawnTimeInterval = 0;
        [self addCrate];
    }
}

- (void)update:(NSTimeInterval)currentTime {
    // Handle time delta.
    // If we drop below 60fps, we still want everything to move the same distance.
    CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
    self.lastUpdateTimeInterval = currentTime;
    if (timeSinceLast > 1) { // more than a second since last update
        timeSinceLast = 1.0 / 60.0;
        self.lastUpdateTimeInterval = currentTime;
    }

    [self updateWithTimeSinceLastUpdate:timeSinceLast];

}

This is the method that it is calling.

- (void)addCrate {

    // Create sprite
        self.crate = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(30, 30)];
        //self.crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.crate.frame.size];

    // Determine where to spawn the crate along the X axis
        int minX = self.crate.size.width / 2;
        int maxX = self.frame.size.width - self.crate.size.width / 2;
        int rangeX = maxX - minX;
        int actualX = (arc4random_uniform(rangeX)) + minX;

    // Create the crate slightly off-screen along the top,
    // and along a random position along the X axis as calculated above
        self.crate.position = CGPointMake(actualX, self.frame.size.height + self.crate.size.height/2);
        [self addChild:self.crate];
        self.crate.size = CGSizeMake(50, 50);


    // Determine speed of the crate
        int actualDuration = 3.5;

    // Create the actions
        SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, -self.crate.size.height/2) duration:actualDuration];
        SKAction * actionMoveDone = [SKAction removeFromParent];
        [self.crate runAction:[SKAction sequence:@[actionMove, actionMoveDone]]];
}

But when I run on my iPhone, only sometimes the tap is registered and the block is removed from the screen, and sometimes it doesn't. Again, I want the node that was tapped on to disappear and only that node.

Thank you!

U1:

-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {

        self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
        [self addCrate];
    }
        return self;
}

- (void)addCrate {

    // Create sprite
        self.crate = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(30, 30)];
    self.crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(30, 30)];
        self.crate.userInteractionEnabled = YES;
        //self.crate.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.crate.frame.size];

    // Determine where to spawn the crate along the X axis
        int minX = self.crate.size.width / 2;
        int maxX = self.frame.size.width - self.crate.size.width / 2;
        int rangeX = maxX - minX;
        int actualX = (arc4random_uniform(rangeX)) + minX;

    // Create the crate slightly off-screen along the top,
    // and along a random position along the X axis as calculated above
        self.crate.position = CGPointMake(actualX, self.frame.size.height + self.crate.size.height/2);
        [self addChild:self.crate];
        self.crate.size = CGSizeMake(50, 50);


    // Determine speed of the crate
        int actualDuration = 3.5;

    // Create the actions
        SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX, -self.crate.size.height/2) duration:actualDuration];
        SKAction * actionMoveDone = [SKAction removeFromParent];
        [self.crate runAction:[SKAction sequence:@[actionMove, actionMoveDone]]];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:self];
    SKNode *touchedNode = [self nodeAtPoint:touchLocation];

    NSLog(@"touchLocation x: %f and y: %f", touchLocation.x, touchLocation.y);

    if (touchedNode != self) {
        NSLog(@"Removed from parent.");
        [touchedNode removeFromParent];
    }
}


- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {

    self.lastSpawnTimeInterval += timeSinceLast;
    if (self.lastSpawnTimeInterval > 1) {
        self.lastSpawnTimeInterval = 0;
        [self addCrate];
    }
}

- (void)update:(NSTimeInterval)currentTime {
    // Handle time delta.
    // If we drop below 60fps, we still want everything to move the same distance.
    CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
    self.lastUpdateTimeInterval = currentTime;
    if (timeSinceLast > 1) { // more than a second since last update
        timeSinceLast = 1.0 / 60.0;
        self.lastUpdateTimeInterval = currentTime;
    }

    [self updateWithTimeSinceLastUpdate:timeSinceLast];

}

1 Answers1

6

I think you should use combination of setting node.name property while creating crates and checking them in touchBegan: method.

Something like this:

SKSpriteNode *crate = [SKSpriteNode spriteNodeWithTexture:tex];
crate.name = @"crate";

And touchBegan: method:

.....
if ([touchedNode.name isEquelToString:@"crate"]){
   // do something with that node
}
.....

Upd1:

Instead of writing this stuff:

if ((location.x > self.crate.frame.origin.x && location.x < self.crate.frame.origin.x + self.crate.frame.size.width) &&
        (location.y > self.crate.frame.origin.y && location.y < self.crate.frame.origin.y + self.crate.frame.size.height)) {

        [self.crate removeFromParent];
    }

use:

if(CGRectContainsPoint(self.frame, touchPoint)){
   // do something
}

Upd2:

Don't see in your code that you are setting userInteractionEnabled = YES on crate nodes.

Upd3:

Here is an example:

//
//  BGMyScene.m
//  Test1
//
//  Created by AndrewShmig on 3/10/14.
//  Copyright (c) 2014 Bleeding Games. All rights reserved.
//

#import "BGMyScene.h"

@implementation BGMyScene

- (id)initWithSize:(CGSize)size
{
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */

        self.backgroundColor = [SKColor colorWithRed:0.15
                                               green:0.15
                                                blue:0.3
                                               alpha:1.0];

//      first label
        SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
//        myLabel.userInteractionEnabled = YES;
        myLabel.text = @"Hello, World!";
        myLabel.fontSize = 30;
        myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
                                       CGRectGetMidY(self.frame));
        [self addChild:myLabel];

//      second label
        SKLabelNode *myLabel2 = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
//        myLabel2.userInteractionEnabled = YES;
        myLabel2.text = @"Hello, World!";
        myLabel2.fontSize = 30;
        myLabel2.position = CGPointMake(100, 100);
        [self addChild:myLabel2];
    }
    return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:self];
    SKNode *touchedNode = [self nodeAtPoint:touchLocation];

    NSLog(@"touchLocation x: %f and y: %f", touchLocation.x, touchLocation.y);

    if (touchedNode != self) {
        NSLog(@"Removed from parent.");
        [touchedNode removeFromParent];
    }
}

- (void)update:(CFTimeInterval)currentTime
{
    /* Called before each frame is rendered */
}

@end

You'll see following screen: enter image description here

After tapping on "Hello, World!" labels they will be removed from parent node.

Community
  • 1
  • 1
AndrewShmig
  • 4,843
  • 6
  • 39
  • 68
  • Yes, I have userInteractionEnabled set to yes. But I made the corrections, it still doesn't work. –  Mar 09 '14 at 17:56
  • In the touchBegan: method, how do I determine which node was touched? –  Mar 09 '14 at 17:57
  • So where do I get 'touchedNode' from? –  Mar 09 '14 at 17:58
  • @IsaRanjha, to determine which node was touched you should (actually you can leave name = nil and use node's userData property) set node's name property after creating it. After that, check in touchBegan: method node name and perform needed actions. See (nodeAtPoint: and nodesAtPoint: methods) https://developer.apple.com/Library/ios/documentation/SpriteKit/Reference/SKNode_Ref/Reference/Reference.html#//apple_ref/occ/instm/SKNode/nodeAtPoint: – AndrewShmig Mar 09 '14 at 18:04
  • Got it. But if I am naming everything the same thing (@"crate") wouldn't they all disappear? How do I know which one was tapped? –  Mar 09 '14 at 18:06
  • @IsaRanjha, see nodeAtPoint: and nodesAtPoint: methods. – AndrewShmig Mar 09 '14 at 18:07
  • So SKNode *node = [self nodeAtPoint:location]; then what do I check with that? –  Mar 09 '14 at 18:09
  • something like: if(node != self) { /* some actions ... */ }. Documentation clearly says that: A descendant in the subtree that intersects the point, or the receiver if no nodes intersect the point. – AndrewShmig Mar 09 '14 at 18:13
  • Okay, so to just make sure I am doing this correctly, what will my final touchesBegan: method look like? –  Mar 09 '14 at 18:15
  • @IsaRanjha, you question was: "Detect what instance of SKSpriteNode was touched?" :) How your method should look - its your decision. After you obtained a node that was touched you can make whatever you want with it. Steps for you: 1) get touch point location 2) get a node that was touched 3) if node != receiver do some actions 4) thats all – AndrewShmig Mar 09 '14 at 18:18
  • I'm confused with step number 2. I don't know how to do this in code. –  Mar 09 '14 at 18:43
  • @IsaRanjha, SKNode *node = [self nodeAtPoint:touchPoint]; where self is your SKScene instance variable. It could be faster if you show us what you have already done and what exactly is not working. – AndrewShmig Mar 09 '14 at 18:57
  • @IsaRanjha, see SKButton.m file ( http://stackoverflow.com/a/19199748/523630 ) and check how touch methods are implemented. This should help you. – AndrewShmig Mar 09 '14 at 19:00
  • So: -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint location = [touch locationInNode:self]; SKNode *node = [self nodeAtPoint:location]; if(node != self) { [self.crate removeFromParent]; } } –  Mar 09 '14 at 22:29
  • @IsaRanjha, so? is it working? You should just try something, experiment and see what you get. – AndrewShmig Mar 09 '14 at 22:45
  • No, that code isn't working. I'm just trying to wrap my was around how that would even work. But it doesn't. It seems like there should be an easier way to do this, as something as simple as tapping a SKSpriteNode shouldn't require a thread on SO. Can I use UIGestureRecognizer for taps? –  Mar 10 '14 at 02:52
  • @IsaRanjha, it is the easiest way. UIGestureRecognizer works with UIViews, so after tapping you also need to get touch point and a node which was under the tap. Uhhh... I'll write an example. – AndrewShmig Mar 10 '14 at 04:20
  • @IsaRanjha, I have updated my answer with working example. See it. – AndrewShmig Mar 10 '14 at 04:32
  • Okay, I must be doing this wrong. I updated my question with what i have, it isn't working, I never get removed from parent in log? –  Mar 10 '14 at 05:04
  • @IsaRanjha, COMMENT all self.crate.userInteractionEnabled = YES or set it to NO. – AndrewShmig Mar 10 '14 at 05:16
  • How do I exempt a SKLabelNode from this? –  Mar 11 '14 at 02:15