将userInteractionEnabled设置为YES的sprite在正常精灵覆盖时不会接收到触摸

时间:2013-10-22 07:10:25

标签: objective-c sprite-kit

我放了一个精灵A(用于接收命中的子类(userInteractionEnabled为YES)),然后是一个普通的精灵B,它不会在其上面点击(userInteractionEnabled默认为NO),完全覆盖精灵A.

点击精灵B,我假设精灵A会触摸但没有任何反应。关于此事的文档部分如下。

我觉得这里有些东西不清楚,因为看起来精灵B仍然接受了触摸但却把它扔了。 或者,spriteA从可能的触摸接收器中移除,因为它不可见。

来自文档: https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/SpriteKit_PG/Nodes/Nodes.html#//apple_ref/doc/uid/TP40013043-CH3-SW7

  

对于在命中测试期间要考虑的节点,它   userInteractionEnabled属性必须设置为YES。默认值   除场景节点外的任何节点都为NO。想要接收的节点   事件需要从它实现适当的响应方法   父类(iOS上的UIResponder和OS X上的NSResponder)。这是一   您必须在其中实现特定于平台的代码的几个地方   精灵套件

无论如何解决这个问题? 只要某个用户的userInteractionEnabled为NO,就不应该干扰其他触摸接收器。

更新:即使将精灵B的alpha设置为0.2,使精灵A非常可见,也不会使精灵A可触摸。 Sprite B完全“吞下”了触摸,尽管没有启用交互。

5 个答案:

答案 0 :(得分:10)

这是我的解决方案,直到Apple以正确的行为更新SpriteKit,或者有人按照我们想要的方式实际知道如何使用它。

https://gist.github.com/bobmoff/7110052

将文件添加到项目中并将其导入Prefix标头中。现在接触应该按照预期的方式工作。

答案 1 :(得分:2)

我的解决方案不需要isKindOfClass类的东西......

在您的SKScene界面中:

@property (nonatomic, strong) SKNode*   touchTargetNode;
// this will be the target node for touchesCancelled, touchesMoved, touchesEnded

在您的SKScene实施中

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch*    touch   = [touches anyObject];
    NSArray*    nodes   = [self nodesAtPoint:[touch locationInNode:self]];

    self.touchTargetNode = nil;

    for( SKNode* node in [nodes reverseObjectEnumerator] )
    {
        if( [node conformsToProtocol:@protocol(CSTouchableNode)] )
        {
            SKNode<CSTouchableNode>*    touchable   = (SKNode<CSTouchableNode>*)node;

            if( touchable.wantsTouchEvents )
            {
                self.touchTargetNode = node;
                [node touchesBegan:touches
                         withEvent:event];
                break;
            }
        }
    }
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.touchTargetNode touchesCancelled:touches
                                 withEvent:event];
    self.touchTargetNode = nil;
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.touchTargetNode touchesMoved:touches
                             withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.touchTargetNode touchesEnded:touches
                                 withEvent:event];
    self.touchTargetNode = nil;
}

您需要定义CSTouchableNode协议...根据您的意愿调用它:)

@protocol CSTouchableNode <NSObject>

- (BOOL) wantsTouchEvents;
- (void) setWantsTouchEvents:(BOOL)wantsTouchEvents;

@end

对于您想要触摸的SKNode,它们需要符合CSTouchableNode协议。您需要根据需要在课程中添加类似下面的内容。

@property (nonatomic, assign) BOOL wantsTouchEvents;

这样做,点击节点可以正常工作。当Apple修复“userInteractionEnabled”错误时,它不会破坏。是的,这是一个错误。一个愚蠢的小虫。傻苹果。

<强>更新

SpriteKit中还有另一个错误......节点的z顺序很奇怪。我已经看到了奇怪的节点排序......因此我倾向于为需要它的节点/场景强制使用zPosition。

我已经更新了我的SKScene实现,以便根据zPosition对节点进行排序。

nodes = [nodes sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"zPosition" ascending:false]]];
for( SKNode* node in nodes )
{
    ...
}

答案 2 :(得分:1)

以下是获取触摸等的示例。

场景

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

        HeroSprite *newHero = [HeroSprite new];
        newHero.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
        [self addChild:newHero];

        SKSpriteNode *overLapSprite = [SKSpriteNode spriteNodeWithColor:[UIColor orangeColor] size:CGSizeMake(70, 70)];
        overLapSprite.position = CGPointMake(CGRectGetMidX(self.frame) + 30, CGRectGetMidY(self.frame));
        overLapSprite.name = @"overlap";
        [self addChild:overLapSprite];
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];

    if ([node.name isEqualToString:@"heroNode"]) {
        NSLog(@"Scene detect hit on hero");
    }
    if ([node.name isEqualToString:@"overlap"]) {
        NSLog(@"overlap detect hit");
    }

    // go through all the nodes at the point
    NSArray *allNodes = [self nodesAtPoint:location];
    for (SKNode *aNode in allNodes) {
        NSLog(@"Loop; node name = %@", aNode.name);
    }
}

Subclassed sprite / Hero

- (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(@"Hero got Hit at, %@ %@", NSStringFromCGPoint(locationA), NSStringFromCGPoint(locationB));
}

@end

尝试过很多东西,但触摸不会经过重叠的精灵。我想你可以使用场景类通过循环遍历节点来检测触摸,然后直接调用该节点。

当粒子发射器覆盖按钮时,我遇到了这个问题...

答案 3 :(得分:1)

我认为这可能是一种设计选择,但肯定不是很灵活。我喜欢在场景级别的Sprite Kit中使用nodeAtPoint处理可以重叠的对象(如游戏对象):结合isKindOfClass:方法。对于通常不重叠并放置在不同图层(如叠加和按钮)的按钮等对象,我在其类中处理用户交互。 我希望我的触摸事件能够在Sparrow Framework中“冒泡”节点树,但我担心它更像是一个功能请求而不是错误报告。

P.S。:通过处理场景级别的触摸,您可以轻松地触摸和移动节点,即使它们被不可移动和不可触摸的节点完全覆盖!

答案 4 :(得分:1)

使用更少行代码的解决方案。请记住在要接收的每个节点中设置userInteractionEnabled。

- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
    CGPoint touchLocation = self.positionInScene;
    SKSpriteNode *touchedNode = (SKSpriteNode *)[self nodeAtPoint:touchLocation];

    NSArray *nodes   = [self nodesAtPoint:touchLocation];
    for (SKSpriteNode *node in [nodes reverseObjectEnumerator]) {
        NSLog(@"Node in touch array: %@. Touch enabled: %hhd", node.name, node.isUserInteractionEnabled);
        if (node.isUserInteractionEnabled == 1)
            touchedNode = node;
    }
    NSLog(@"Newly selected node: %@", touchedNode);
}