Custom SKSpriteNode not detected during touch

时间:2023-01-23 07:44:30

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.

在学习SpriteKit时,我正在尝试制作一款小型冒险游戏。我正在创建一个英雄,并将其添加到场景中,然后在touchesBegan:中,我检测到触摸是否源自英雄,并采取相应的操作。

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:

如果我将英雄添加为SKSpriteNode,则触摸会检测到他。如果我将他添加为SKSpriteNode的子类,则触摸不会!添加的区别:

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

vs

_heroNode = [[ADVHeroNode alloc] init];

The init looks like this:

init看起来像这样:

- (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:

将英雄添加为SKSpriteNode的子类可以将其添加到场景中,但触摸不会检测到他。我的touchesBegan:看起来像这样:

-(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?

令人沮丧的是,这个代码在添加一个直接的SKSpriteNode时工作得很好,而不是我自己的子类。有什么建议?

5 个解决方案

#1


23  

Here are some example ways to subclass your Hero

以下是一些子类化Hero的示例方法

SKNode

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

子类化SKNode此方法需要您的节点监视触摸,因此需要self.userInteractionEnabled属性。

@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.

另一种“更简单”的方式,如果您只想子类化SKSpriteNode。这将与您习惯使用的基本相同(在您想要继承Hero之前)。所以你的touchesBegan,在你的问题中设置将起作用。

@implementation HeroSprite

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

    }
    return self;
}

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

@end

#2


10  

Just be sure to add this in your init method.

请务必在init方法中添加它。

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.

由于某些原因,在子类化时默认情况下不启用此选项。至少对我来说,它仅在手动启用时才有效。

#3


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;
    }

#4


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.

当您创建一个Object SKSpriteNode时,它将控制自己。这意味着,当触摸开始时,它意味着此对象接收此触摸而不是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.

因此,如果要检测此触摸,则必须在此对象上编写此代码,而不是在SKView上。如果您想在SKSpriteNode上进行触摸时在SKView上执行任何操作,则可以使用委托执行此操作。

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

如果您无法理解我的答案,请随时询问更多。

#5


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.

您的ADVHeroNode是没有图像的SkNode或SkSpriteNode。它们都没有任何范围,它们可以说是重点。 nodeAtPoint将找不到它们:但是图像子SKSpriteNode将会找到它们。由于您明确检查节点的名称,因此检查失败。

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.

您可以为图像指定名称,然后检查它,然后引用其父级以获取ADVHeronode实例。或者直接检查node.parent的名称。

#1


23  

Here are some example ways to subclass your Hero

以下是一些子类化Hero的示例方法

SKNode

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

子类化SKNode此方法需要您的节点监视触摸,因此需要self.userInteractionEnabled属性。

@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.

另一种“更简单”的方式,如果您只想子类化SKSpriteNode。这将与您习惯使用的基本相同(在您想要继承Hero之前)。所以你的touchesBegan,在你的问题中设置将起作用。

@implementation HeroSprite

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

    }
    return self;
}

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

@end

#2


10  

Just be sure to add this in your init method.

请务必在init方法中添加它。

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.

由于某些原因,在子类化时默认情况下不启用此选项。至少对我来说,它仅在手动启用时才有效。

#3


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;
    }

#4


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.

当您创建一个Object SKSpriteNode时,它将控制自己。这意味着,当触摸开始时,它意味着此对象接收此触摸而不是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.

因此,如果要检测此触摸,则必须在此对象上编写此代码,而不是在SKView上。如果您想在SKSpriteNode上进行触摸时在SKView上执行任何操作,则可以使用委托执行此操作。

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

如果您无法理解我的答案,请随时询问更多。

#5


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.

您的ADVHeroNode是没有图像的SkNode或SkSpriteNode。它们都没有任何范围,它们可以说是重点。 nodeAtPoint将找不到它们:但是图像子SKSpriteNode将会找到它们。由于您明确检查节点的名称,因此检查失败。

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.

您可以为图像指定名称,然后检查它,然后引用其父级以获取ADVHeronode实例。或者直接检查node.parent的名称。