1

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?

NieLernend
  • 190
  • 9

1 Answers1

1

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

0x141E
  • 12,613
  • 2
  • 41
  • 54
  • But why is SpriteKit so inconsistent? I expected that the return of the nodeAtPoint: would either be the node i see at the point or another consistent result. Like the use of the accumulated frame. Now it does highly depend on the nodes type, which may be even different for other node types ... – NieLernend Aug 19 '15 at 10:03
  • If you need consistent behavior, you can use the same node subclass (e.g., SKSpriteNode) for all your game entities. You can create the shapes (e.g., circles, polygons) with an image editing app. – 0x141E Aug 20 '15 at 08:41
  • That is unfortunately not appropriate for my use. I build an framework that allows me to recognise touches of nearly any input hardware in MacOS SpriteKit Applications. So i can not restrict every user of my framework to use only SpriteNodes. This inconsistency problem arises when i try identify which node was pressed. Probably i will make it consistent by not using nodeAtPoint, but by checking the accumulated frames on my own. It is a shame, but still thanks for the help. – NieLernend Aug 20 '15 at 08:48
  • Also one additional question. From where do you know that containsPoint: will use the shapes path? The documentation states that containsPoint: should actually use the bounding box ... – NieLernend Aug 24 '15 at 15:07
  • 1
    I created a project that displays the results of `containsPoint:` similar to what I did with `nodeAtPoint:` in Figure 2. By clicking in and around the circle node, I concluded that Sprite Kit is using the circle's path instead of its bounding box. – 0x141E Aug 24 '15 at 20:13