0

I searched trough SO and tried few examples, but I still can't understand this behaviour. On simulator 7.1 tap-through works, but on 8.1 don't work.Also I asked earlier similar question, but not the same as this and I solved it using nodesAtPoint method and then looping trough all nodes and checking node name / class... But this differs because now I use custom Button class which implements touchesBegan and I want it to detect and if possible "swallow" touches.

So I have a simple Button class which is subclass of SKSpriteNode and it has it's own touchesBegan and userInteractionEnabled = YES. In my view controller property ignoreSiblingsOrder is set to YES.

Here is an (simplified) example which can produce described behaviour:

#import "GameScene.h"

@interface Button : SKSpriteNode
-(instancetype)initWithColor:(UIColor *)color size:(CGSize)size;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
@end


@implementation Button
-(instancetype)initWithColor:(UIColor *)color size:(CGSize)size {
    self = [super initWithColor:color size:size];
    self.userInteractionEnabled = YES;
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"%@ hit", self.name);
}
@end

@implementation GameScene

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

        self.userInteractionEnabled = NO;

        SKNode* root = [SKNode new];
        root.name = @"root";
        SKNode* layer1 = [SKNode new];
        SKNode* layer2 = [SKNode new];

        layer1.zPosition = -1;//layer1 and layer2 are just containers
        layer2.zPosition = -2;


        Button* button = [Button spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(100, 100)];
        button.name = @"yellow button";
        button.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));

        [layer1 addChild:button];

        [root addChild:layer1];
        [root addChild:layer2];

        [self addChild:root];

    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSLog(@"Touch detected");
}

@end

I just don't understand why this doesn't work on 8.1...I know that hit-testing goes in opposite order then rendering nodes, but then what would be the right way to achieve tap-through behaviour? So currently what's happening is when I test on 7.1 I got message "yellow button", but on 8.1 I got message "touch detected" (and when I print node name it says root). Also I have been pointed to file a radar because of this, but as I said I solved everything with nodesAtPoint instead of nodeAtPoint so I didn't. And because I thought that that's not a bug, but rather my mistake, because on 7.1 everything was fine. So is this a bug, or something else ?

4

1 回答 1

1

具有讽刺意味的是,根据我的数学,这似乎是 7.1 而不是 8.1 的错误。不过,我还没有用 7.1 测试过。

首先,我无法使用您的任何代码,self.userInteractionEnabled = NO;因为没有任何东西会收到触摸。

zPosition 在设置时使用,ignoreSiblingsOrder但它基于其父级。因此,如果父级为 -1,而您添加一个子级为 0,则其渲染 zPosition 仍为 -1。触摸也是如此,但顺序相反。最后一个用 userInteraction 渲染的获取触摸事件。

希望这是有道理的。请参阅添加的注释和调试。

#import "GameScene.h"

@interface Button : SKSpriteNode
-(instancetype)initWithColor:(UIColor *)color size:(CGSize)size;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
@end


@implementation Button
-(instancetype)initWithColor:(UIColor *)color size:(CGSize)size {
    self = [super initWithColor:color size:size];
    self.userInteractionEnabled = YES;
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSLog(@"%@ hit", self.name);
}
@end

@implementation GameScene

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

        SKNode* root = [SKNode new];
        root.name = @"root";

        SKNode* layer1 = [SKNode new];
        layer1.name = @"layer1";

        SKNode* layer2 = [SKNode new];
        layer2.name = @"layer2";

        layer1.zPosition = -1;//layer1 and layer2 are just containers
        layer2.zPosition = -2;


        Button* button = [Button spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(100, 100)];
        button.name = @"yellow button";
        button.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));

        //layer1 is at -1 and button does not have a z so it will be -1 in the scene like its parent (-1+0)
        [layer1 addChild:button];

        //root is 0 layer1 is -1 (along with button) root is above layer1 and will recieve any touches
        [root addChild:layer1];

        //root is 0 layer2 is -2 layer1 (and button) are above layer2 and root is above layer1 root gets touch
        [root addChild:layer2];

        //nothing changes except root is added
        [self addChild:root];

        //button needs to be on same zPosition or higher to get touch
        //it is -1 because of parent node + 1 = 0
        //best if you do +2 to ensure it is above

//        button.zPosition = 2;

    }
    return self;
}

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

    for (UITouch *touch in touches)
    {
        CGPoint point = [touch locationInNode:self];
        SKNode *node = [self nodeAtPoint:point];
        NSLog(@"Touch detected: %@", node.name);
    }

}

另外我建议不要对 zPosition 使用负数。它确实使事情变得更加混乱。

于 2015-04-04T01:29:49.940 回答