SKNode nodeAtPoint: / containsPoint: не одинаковое поведение для SKSpriteNode и SKShapeNode

nodeAtPoint: дает разные результаты при использовании SKShapeNode и SKSpriteNode. Если я прав, nodeAtPoint: будет использовать containsPoint:, чтобы проверить, какие узлы находятся в данной точке.

docu указывает, что containsPoint: будет использовать свою ограничивающую рамку.

Я настроил простую сцену, где в ситуации 1 круг является родителем фиолетового узла, а в ситуации 2 зеленый узел является родителем фиолетового узла. В обоих случаях я щелкнул в области, где должна быть ограничивающая рамка родителя.

Результат отличается. Если я использую SKSpriteNode, nodeAtPoint: даст мне родителя. Если я использую SKShapeNode, он возвращает SKScene.

(Крестик отмечает место, где я нажимал мышью.)

введите здесь описание изображения введите здесь описание изображения

Код:

Первая настройка:

-(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];
}

Вторая установка:

-(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];

}

Вызов по щелчку мыши:

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

Я что-то пропустил? Это ошибка в SpriteKit? Это должно работать таким образом?


person NieLernend    schedule 14.08.2015    source источник


Ответы (1)


Краткие ответы: да, нет, да

Длинный ответ...

В документации для nodeAtPoint сказано, что это

возвращает самого глубокого потомка, который пересекает точку

и в разделе Обсуждение

точка считается находящейся в узле, если она находится внутри прямоугольника, возвращаемого методом calculateAccumulatedFrame.

Первое утверждение применяется к узлам SKSpriteNode и SKShapeNode, а второе — только к узлам SKSpriteNode. Для SKShapeNodes Sprite Kit игнорирует ограничивающую рамку узла и использует свойство path, чтобы определить, пересекает ли точка узел с CGPathContainsPoint. Как показано на рисунках ниже, фигуры выбираются для каждого пикселя, где белые точки представляют собой точки щелчка.

Ограничительная рамка для узла формы

Рис. 1. Ограничивающие рамки для формы (синяя) и фигуры + квадрата (коричневая)

containsPoint Test

Рисунок 2. Результаты nodeAtPoint

calculateAccumulatedFrame возвращает ограничивающую рамку (BB), которая находится относительно своего родителя, как показано на рисунке ниже (коричневая рамка — это BB квадрата). Следовательно, если вы не настроите CGPoint на containsPoint соответствующим образом, результаты будут не такими, как вы ожидали. Чтобы преобразовать точку из координат сцены в координаты родителя (или наоборот), используйте convertPoint:fromNode или convertPoint:toNode. Наконец, containsPoint использует path фигуры вместо ограничивающей рамки, как nodeAtPoint.

введите здесь описание изображения

person 0x141E    schedule 15.08.2015
comment
Но почему SpriteKit такой непоследовательный? Я ожидал, что возврат nodeAtPoint: будет либо узлом, который я вижу в точке, либо другим согласованным результатом. Нравится использование накопленного кадра. Теперь это сильно зависит от типа узлов, который может быть даже другим для других типов узлов... - person NieLernend; 19.08.2015
comment
Если вам нужно согласованное поведение, вы можете использовать один и тот же подкласс узла (например, SKSpriteNode) для всех ваших игровых объектов. Вы можете создавать фигуры (например, круги, многоугольники) с помощью приложения для редактирования изображений. - person 0x141E; 20.08.2015
comment
К сожалению, это не подходит для моего использования. Я создаю фреймворк, который позволяет мне распознавать касания практически любого оборудования ввода в приложениях MacOS SpriteKit. Поэтому я не могу запретить каждому пользователю моего фреймворка использовать только SpriteNodes. Эта проблема несоответствия возникает, когда я пытаюсь определить, какой узел был нажат. Вероятно, я сделаю это согласованным, не используя nodeAtPoint, а самостоятельно проверяя накопленные кадры. Обидно, но все равно спасибо за помощь. - person NieLernend; 20.08.2015
comment
Также один дополнительный вопрос. Откуда вы знаете, что containsPoint: будет использовать путь формы? В документации указано, что containsPoint: должен фактически использовать ограничительную рамку... - person NieLernend; 24.08.2015
comment
Я создал проект, который отображает результаты containsPoint: аналогично тому, что я сделал с nodeAtPoint: на рисунке 2. Нажав на узел круга и вокруг него, я пришел к выводу, что Sprite Kit использует путь круга вместо его ограничивающей рамки. - person 0x141E; 24.08.2015