1
votes

The nodeAtPoint: gives not the same result if using SKShapeNode and SKSpriteNode. If i am correct nodeAtPoint: will use containsPoint: to check which nodes are at the given point.

The docu states that containsPoint: will use its bounding box.

I set up a simple scene, where in situation 1 the circle is parent of the purple node and in situation 2 the green node is parent of the purple node. I clicked in both cases in an area where the bounding box of the parent should be.

The result is differs. If i use a SKSpriteNode the nodeAtPoint: will give me the parent. If i use SKShapeNode it returns the SKScene.

(The cross marks where i pressed with the mouse.)

enter image description here enter image description here

The code:

First setup:

-(void)didMoveToView:(SKView *)view {
self.name = @"Scene";

SKShapeNode* circle = [SKShapeNode node];
circle.path = CGPathCreateWithEllipseInRect(CGRectMake(0, 0, 50, 50), nil);
circle.position = CGPointMake(20, 20);
circle.fillColor =  [SKColor redColor];
circle.name = @"circle";

SKSpriteNode* pnode = [SKSpriteNode node];
pnode.size = CGSizeMake(50, 50);
pnode.position = CGPointMake(50, 50);
pnode.color =  [SKColor purpleColor];
pnode.name = @"pnode";


[self addChild: circle];
[circle addChild: pnode];
}

Second setup:

-(void)didMoveToView:(SKView *)view {
self.name = @"Scene";

SKSpriteNode* gnode = [SKSpriteNode node];
gnode.size = CGSizeMake(50, 50);
gnode.position = CGPointMake(30, 30);
gnode.color =  [SKColor greenColor];
gnode.name = @"gnode";

SKSpriteNode* pnode = [SKSpriteNode node];
pnode.size = CGSizeMake(50, 50);
pnode.position = CGPointMake(30, 30);
pnode.color =  [SKColor purpleColor];
pnode.name = @"pnode";


[self addChild: gnode];
[gnode addChild: pnode];

}

Call on mouse click:

-(void)mouseDown:(NSEvent *)theEvent {
CGPoint location = [theEvent locationInNode:self];
NSLog(@"%@", [self nodeAtPoint: location].name);
}

Did i miss something? Is it a bug in SpriteKit? Is it meant to work that way?

1

1 Answers

1
votes

The short answers: yes, no, yes

The long answer...

The documentation for nodeAtPoint says that it

returns the deepest descendant that intersects a point

and in the Discussion section

a point is considered to be in a node if it lies inside the rectangle returned by the calculateAccumulatedFrame method

The first statement applies to SKSpriteNode and SKShapeNode nodes, while the second only applies to SKSpriteNode nodes. For SKShapeNodes, Sprite Kit ignores the node's bounding box and uses the path property to determine if a point intersects the node with CGPathContainsPoint. As shown in the figures below, shapes are selected on a per-pixel basis, where white dots represent the click points.

Bounding Box for Shape Node

Figure 1. Bounding Boxes for Shape (blue) and Shape + Square (brown)

containsPoint Test

Figure 2. Results of nodeAtPoint

calculateAccumulatedFrame returns a bounding box (BB) that is relative to its parent as shown in the figure below (brown box is the square's BB). Consequently, if you don't adjust the CGPoint for containsPoint appropriately, the results will not be what you expected. To convert a point from scene coordinates to the parent's coordinates (or vice versa), use convertPoint:fromNode or convertPoint:toNode. Lastly, containsPoint uses a shape's path instead of its bounding box just like nodeAtPoint.

enter image description here