Bug — hit-testing doesn't work as intended when siblings overlap:
There are 2 overlapping nodes in a scene which have the same parent (ie. siblings)
The topmost node has
userInteractionEnabled = NO
whilst the other node hasuserInteractionEnabled = YES
.If the overlap is touched, after the topmost node is hit-tested and fails (because
userInteractionEnabled = NO
), instead of the bottom node being the next to be hit-tested, it is skipped and the parent of the 2 siblings is hit-tested.What should happen is that the next sibling (the bottom node) is hit-tested rather than the hit-test jumping to the parent.
According to the Sprite Kit documentation:
"In a scene, when Sprite Kit processes touch or mouse events, it walks the scene to find the closest node that wants to accept the event. If that node doesn’t want the event, Sprite Kit checks the next closest node, and so on. The order in which hit-testing is processed is essentially the reverse of drawing order. For a node to be considered during hit-testing, its userInteractionEnabled property must be set to YES. The default value is NO for any node except a scene node."
This is a bug as siblings of a node are rendered before their parents — a sibling should be the next to be tested, and not its parent. In addition, if a node has userInteractionEnabled = NO
, then surely it should be 'transparent' with regards to hit-testing — but here it is not as it results in a change of behaviour as a node is skipped over in the test.
I have searched online, but can't find anyone also reporting or posting about this bug. So should I report this?
And then the reason why I've posted this here is because I would like a suggestion for a 'fix' of this bug (ie. a suggestion for an implementation of some code somewhere so that SpriteKit works in the 'intended' manner for hit-testing)
To replicate the bug:
Use the "Hello World" template provided when you start a new "Game" project in Xcode (it has "Hello World" and adds rocket sprites when you click).
Optional: [I also deleted the rocket sprite image from the project as the rectangle with the
X
which occurs when the image isn't found is easier to work with for debugging, visually]Add a SKSpriteNode to the scene with
userInteractionEnabled = YES
(I'll refer to it as Node A from now on).Run the code.
You'll notice that when you click on Node A, no rocket sprites are spawned. (expected behaviour since the hit-test should stop after it is successful - it stops as it succeeds on Node A.)
However, if you spawn a few rockets which are next to Node A, and then click on a place where Node A and a rocket overlaps, it is then possible to spawn another rocket on top of Node A — but this shouldn't be possible. This means that after the hit-test fails on the topmost node (the rocket which has
userInteractionEnabled = NO
by default), instead of testing Node A next, it tests the parent of the rocket instead which is the Scene.
Note: I am using Xcode 7.3.1, Swift, iOS — I haven't tested to see if this bug is universal, yet.
Extra detail: I did some additional debugging (slight complication to the replication above) and determined that the hit-test is sent to the parent afterwards and therefore not necessarily to the scene.
userInteractionEnabled
property is meant to represent transparency with regards to hit-testing, which fails because of this behaviour. – Ambrosia