Kieran Crown
Kieran Crown

Reputation: 317

Detect touch within a radius of a node in SpriteKit

I have a player object in my SpriteKit game and at the moment I'm moving it using the following code:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (touches.count == 1)
    {
        [self touchesMoved:touches withEvent:event];
    }
}


-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    if (touches.count == 1)
    {
        [_player runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self] duration:0.01]];
    }
}

But I only want to move the player node when the touch is within a specific range of the location.

Upvotes: 2

Views: 1652

Answers (3)

Mike S
Mike S

Reputation: 42325

Since it looks like you only care about the distance of the touch from your _player object, I would move the radius detection logic to your _player object and use an SKRegion to handle it.

Note: If you need to know if your touch is within the radius of multiple nodes then you might want to use a method from this question instead.

To use an SKRegion to determine if your touch is within a certain radius of _player, add an SKRegion instance to your _player object and initialize it with the radius you want. In [_player init] or wherever it makes the most sense for your project:

self.touchRegion = [[SKRegion alloc] initWithRadius:radius];

Then add a method to determine if a point is within that region:

- (BOOL)pointIsInTouchRegion:(CGPoint)point {
    return [self.touchRegion containsPoint: point];
}

Next change your touchesMoved method to something like:

-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    if (touches.count == 1)
    {
        UITouch *touch = [touches anyObject];
        CGPoint point = [self locationInNode:_player]; // NOTE: It's important to use _player here!
        if ([_player pointIsInTouchRegion: point]) {
            [_player runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self] duration:0.01]];
        }
    }
}

Note: Using _player in [self locationInNode:_player]; is critical since your SKRegion will be defined in the _player node's coordinate system.

One more note: You don't really have to put all that logic in _player, that's just the way I would do it. You could just as easily store the SKRegion in your scene's class and call [region containsPoint: point] on it directly.


Update: iOS 7 method

The main difference with doing this for iOS 7 is that you need to store the actual radius you're interested in rather than an SKRegion (since it's only available in iOS 8), and then do the calculation yourself in pointIsInTouchRegion:.

So, in _player, set your touch radius where you were setting the touchRegion before:

self.touchRadius = radius

Then change the pointIsInTouchRegion: method so that it does the actual distance calculation.

- (BOOL)pointIsInTouchRegion:(CGPoint)point {
    CGPoint centerOfNode = CGPointMake(self.frame.size.width / 2.0, self.frame.size.height / 2.0); // NOTE: If this is in an SKSpriteNode, you might want to use self.anchorPoint instead
    CGFloat distanceToCenter = return hypotf(point.x - centerOfNode.x, point.y - centerOfNode.y);
    return (distanceToCenter <= self.radius);
}

The code in touchesMoved should not need any changes.

Upvotes: 3

0x141E
0x141E

Reputation: 12753

The following demonstrates how to 1) select a sprite that is within a specified distance from a touch event and 2) drag and drop the selected sprite to a new location.

@interface MyScene()

@property (weak) SKSpriteNode *selectedNode;

@end

@implementation MyScene

-(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];

        // Add three sprites to scene
        SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(32, 32)];
        sprite.name = @"Blue";
        sprite.position = CGPointMake(CGRectGetMidX(self.frame),
                                      CGRectGetMidY(self.frame)-64);
        [self addChild:sprite];

        sprite = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(32, 32)];
        sprite.name = @"Red";
        sprite.position = CGPointMake(CGRectGetMidX(self.frame),
                                      CGRectGetMidY(self.frame) + 64);
        [self addChild:sprite];


        sprite = [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(32, 32)];
        sprite.name = @"Green";
        sprite.position = CGPointMake(CGRectGetMidX(self.frame),
                                      CGRectGetMidY(self.frame));

        [self addChild:sprite];
    }
    return self;
}

#define kRadius     64

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    _selectedNode = nil;
    CGFloat minDistance = kRadius+1;
    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        for (SKSpriteNode *node in self.children) {
            CGFloat dx = location.x - node.position.x;
            CGFloat dy = location.y - node.position.y;
            // Compute distance from sprite to tap point
            CGFloat distance = sqrtf(dx*dx+dy*dy);
            if (distance <= kRadius) {
                if (distance < minDistance) {
                    // Select the closest node so far
                    _selectedNode = node;
                    minDistance = distance;
                }
            }
        }
    }
}

- (void) touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    if (_selectedNode) {
        // Move selected node to current touch position
        _selectedNode.position = location;
    }
}

- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    // Unselect node
    _selectedNode = nil;
}

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

@end

Upvotes: 1

Darin
Darin

Reputation: 29

I think you should add this, forgive me if I mis understand your question however:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
   CGPoint point = [touch locationInView:self.view];

if (touches.count == 1) && (point.x == yourObjectsXPosition) && (point.y ==  yourObjectsYPosition) 

     {
    [self touchesMoved:touches withEvent:event];
     }
}

Upvotes: 0

Related Questions