1

Edit: Apple fixed this in iOS8, so it was certainly not expected/intended but a bug.

It appears that the zPosition is interpreted differently when used for rendering a node vs when used for determining which node is touched.

When I create a hierarchy of parent and childnodes (all with zPosition set), the topmost node for rendering is determined by the summation of all zPositions leading to the node, so nodes from different parents can be interleaved.
When determining the topmost node for receiving touches however, zPosition is only used to distinguish between siblings of the same parent, when nodes have a different parent, their parents z-order takes precedence. (all childs of one parent are all underneath or above the nodes of a different parent)

To illustrate, some sample code with results:

#import "MyScene.h"

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


@implementation NodeWithHitTest
-(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 MyScene

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        SKNode* container1 = [SKNode new];
        SKNode* container2 = [SKNode new];
        NodeWithHitTest* sprite1 = [NodeWithHitTest spriteNodeWithColor:[UIColor yellowColor] size:CGSizeMake(200, 200)];
        sprite1.name = @"yellow";
        sprite1.position = CGPointMake(self.size.width/2, self.size.height/2);
        NodeWithHitTest* sprite2 = [NodeWithHitTest spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(150, 150)];
        sprite2.name = @"blue";
        sprite2.position = sprite1.position;
        NodeWithHitTest* sprite3 = [NodeWithHitTest spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(100, 100)];
        sprite3.name = @"red";
        sprite3.position = sprite1.position;

        [container1 addChild:sprite1];
        [container1 addChild:sprite3];
        [container2 addChild:sprite2];

        [self addChild:container1];
        [self addChild:container2];
//        sprite1.zPosition = 1;
//        sprite2.zPosition = 2;
//        sprite3.zPosition = 3;
    }
    return self;
}

@end

When run with zPosition lines commented out, you get the following result:
without zPosition

As expected, first container1 is rendered (with a yellow and red square), and on top container2 is rendered (with blue square), so the red square gets hidden. When clicking inside the yellow 'border', the output is yellow hit, clicking inside the blue sqaure prints blue hit, all as expected.

When the zPosition lines are inserted however, you get the following:
with zPosition

As you can see, the blue square from container2 is interleaved with the two squares of container1, when you click inside the red square however, you get blue hit!
When determining which node is topmost for touches, everything from container2 is still above container1.

Is this expected behaviour? And if so, is there a common way to deal with this without losing one's sanity?

Pieter
  • 17,435
  • 8
  • 50
  • 89

2 Answers2

2

I do not know if anything changed since your post or if you just did something wrong, but in my case it worked.

I just copy paste your code in a fresh project and tested it.

I get red hit if the red square is at top and i get blue square if blue square is at top.

enter image description here

enter image description here

NieLernend
  • 190
  • 9
  • I retested this in iOS 8.3 and you're right, it's fixed. I reran the same project on an iOS 7.1.2 device and there it still says "blue hit" when the red square is shown, so it only works correctly on anything not running iOS7. Thanks for checking it out – Pieter Apr 22 '15 at 09:29
0

Yes, this is expected behavior. The z-positions for drawing and for touch are not identical. While there is something like a global z-position for drawing the one for determining which node was touched is relative to the parent (and their z-relation to one another).

Ralf
  • 1
  • 1
  • This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment). – chopper Oct 10 '14 at 00:06
  • Do you have any source this is intended behaviour? It feels very odd and unintuitive that tapping the red square is interpreted as a tap for the blue square – Pieter Oct 10 '14 at 06:55
  • @chopper: Sorry to have used the system in the wrong way. I should've commented on the original post and not provided an answer that is none. Pieter: I found this out after many hours of trial & error in my own project. And yes, it is counter-intuitive. The only reason why I believe it was done this way is that when you start using different kinds of SKNodes (like SKEffectNodes and SKCropNodes) it might make sense to have the 'touch z-order' different from the 'draw z-order'. But this is only speculation not knowledge. – Ralf Oct 17 '14 at 21:42