13

In learning SpriteKit, I am trying to make a small adventure game. I am creating a hero, and adding it to the scene, and then later, during touchesBegan:, I detect if the touch originated on the hero, and take actions accordingly.

If I add the hero as a SKSpriteNode the touch detects him. If I add him as a subclass of SKSpriteNode the touch does not! The difference in adding:

_heroNode = [SKSpriteNode spriteNodeWithImageNamed:@"hero.png"];

vs

_heroNode = [[ADVHeroNode alloc] init];

The init looks like this:

- (id) init {
    if (self = [super init]) {

        self.name = @"heroNode";
        SKSpriteNode *image = [SKSpriteNode spriteNodeWithImageNamed:@"hero.png"];
        [self addChild:image];
    }
    return self;
}

Adding the hero as a subclass of SKSpriteNode works in the sense that it is added to the scene, but the touch doesn't detect him. My touchesBegan: looks like this:

-(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];
    if (YES) NSLog(@"Node name where touch began: %@", node.name);

    //if hero touched set BOOL
    if ([node.name isEqualToString:@"heroNode"]) {
        if (YES) NSLog(@"touch in hero");
        touchedHero = YES;
    }
}

Frustratingly, this code works just fine when adding a straight up SKSpriteNode, and not my own subclass of it. Any suggestions?

67cherries
  • 6,931
  • 7
  • 35
  • 51
zeeple
  • 5,509
  • 12
  • 43
  • 71

5 Answers5

24

Here are some example ways to subclass your Hero

SKNode

Subclass an SKNode this method requires your node to monitor for touches, hence the self.userInteractionEnabled property.

@implementation HeroSprite

- (id) init {
    if (self = [super init]) {
        [self setUpHeroDetails];
        self.userInteractionEnabled = YES;
    }
    return self;
}

-(void) setUpHeroDetails
{
    self.name = @"heroNode";
    SKSpriteNode *heroImage = [SKSpriteNode spriteNodeWithImageNamed:@"Spaceship"];
    [self addChild:heroImage];
}


-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint locationA = [touch locationInNode:self];
    CGPoint locationB = [touch locationInNode:self.parent];
    NSLog(@"Hit at, %@ %@", NSStringFromCGPoint(locationA), NSStringFromCGPoint(locationB));
}

@end

SKSpriteNode

The other "easier" way, if you just want to subclass a SKSpriteNode. This will work essentially the same as you are use to (before you wanted to subclass your Hero). So your touchesBegan, as set up in your question will work.

@implementation HeroSprite

- (id) init {
    if (self = [super init]) {
        self = [HeroSprite spriteNodeWithImageNamed:@"Spaceship"];
        [self setUpHeroDetails];

    }
    return self;
}

-(void) setUpHeroDetails {
    self.name = @"heroNode";
}

@end
DogCoffee
  • 19,820
  • 10
  • 87
  • 120
14

Just be sure to add this in your init method.

self.userInteractionEnabled = YES;

For some reasons this is not enabled by default when you subclass. At least for me it worked only when manually enabled.

Fawkes
  • 3,831
  • 3
  • 22
  • 37
3

I usually check the touched node's parent until i find the class i want to control. Here's a code snippet.

while (touchedNode.parent)
    {
        if ([touchedNode isKindOfClass:[GameObject class]])
            break;
        else
            touchedNode = (SKSpriteNode*)self.touchedNode.parent;
    }
membersheep
  • 504
  • 4
  • 12
1

It just detected touch on this sprite only.

When you create an Object SKSpriteNode, it will control itself. It mean, when a touch begin on it, it mean this object receive this touch not the SKView.

So if you want to detect this touch, you must write this code on this object not on the SKView. If you want to do anything on SKView when a touch happen on SKSpriteNode, you can use delegate to do this.

Feel free to ask more if you cannot understand what I answer.

Huygamer
  • 162
  • 1
  • 8
  • altho the OP never asked for this, I'd love to see the code of your delegate solution you're talking about. I'm successfully handling touchEnded in my SKSpriteNode subclass but would like to also trigger the touchesEnded method in the SKView, which is not the case if you enable userInteractionEnabled on the subclass. – stvn Feb 18 '14 at 23:41
0

Your ADVHeroNode is either a SkNode or SkSpriteNode with no image. Neither will have any extents, they're points so to speak. They will not be found by nodeAtPoint: but the image child SKSpriteNode will. Since you're explicitly checking for the node's name, the check fails.

You could give the image a name, and check for that and then refer to its parent to gethe ADVHeronode instance. Or just check node.parent's name directly.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217