Reputation: 3995
I'm trying to get the nodes at at a touch location, but am running into problems with .nodes()
returning parent nodes and other nodes at the same "location."
I've tried a dozen different code configurations, but can't seem to find the right sequence of using SKScene.convert()
or SKScene.convertPoint()
and others....
Basically, I have a hierarchical tree of nodes vertically.. the top is the parent and each underneath is a child of the one directly on top... (see pic below)
When I click on the very bottom node, I get multiple hits from running .nodes()
in my scene... I'm trying to get ONLY the nodes at the exact location that I click... When I click somewhere I don't want the parent nodes or other nodes at the same "location" or same ".position" to print... I'm just trying to get ONLY the nodes at the EXACT position I touch...
Here is a picture of what I'm working with... when you click/tap the screen, it prints out which nodes are at the touch location:
Here is my code on click (remember, each node is in parent / child from top to bottom, and it's SKNode.name
is just a number as an identifier):
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let allNodes = nodes(at: touches.first!.location(in: self))
for node in allNodes { print( node.name! ) }
}
I've tried a dozen different ways to convert the touch point to UIView
or SKNode
coords, etc.. I can't get it right... here is the output when I click the bottom-right node:
(again, the numbers are just the name of the node in creation order...):
8
7
5
4
2
1
There should only be 1-2 nodes printed when I click the bottom right node, but instead I am getting 6 nodes printed... and this is because of their .postion
being the "same" I believe in the messed up coord system of SK at times =/
So, my workaround is to create a filter that work based on the Y values ... so in other words, only nodes around the general area of where I clicked will be printed... BUT my operation is already intensive, so I'm trying to NOT have to create a filter for this list...
I really think that there is a way to JUST GET THE NODES AT WHERE I CLICK
but I haven't been able to figure out the right combination of .nodes()
and .location(in:)
to get this right.
I've tried playing with .atPoint
and using the .position
from there, but no luck at all..
I'm lost right now, and don't want to have to create a filer :{
Upvotes: 3
Views: 413
Reputation: 35382
If I've understand in your project you have nodes where each node have a different parent. When you click on the screen you touched a specific point, but this point is detected on the current scene 'self' (the class where you have touchesBegan
) so when you touch the deepest node in releationship parent/child the node touched showed also his parents.
But you want only the node/nodes involved in the touch point visible zone, so I think you could doing in this case is to obtain the frame of each node converted to the current class (self) and check if the touch point is contained to this frame.
In this example below I use 4 nodes:
import SpriteKit
class GameScene: SKScene {
var node5:SKSpriteNode!
var node6:SKSpriteNode!
var node7:SKSpriteNode!
var node8:SKSpriteNode!
override func didMove(to view: SKView) {
let startPos = CGPoint(x:self.frame.midX,y:self.frame.midY)
node5 = SKSpriteNode.init(color: .yellow, size: CGSize(width:50,height:60))
self.addChild(node5)
node5.name = "5"
node5.position = CGPoint(x:startPos.x+35,y:startPos.y-180)
node6 = SKSpriteNode.init(color: .blue, size: CGSize(width:50,height:60))
node5.addChild(node6)
node6.name = "6"
node6.position = CGPoint(x:60,y:0)
node7 = SKSpriteNode.init(color: .green, size: CGSize(width:80,height:60))
node6.addChild(node7)
node7.name = "7"
node7.position = CGPoint(x:60,y:10)
node8 = SKSpriteNode.init(color: .red, size: CGSize(width:80,height:60))
node7.addChild(node8)
node8.name = "8"
node8.position = CGPoint(x:-10,y:20)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let allNodes = nodes(at: positionInScene)
for node in allNodes {
let nodePositionConverted = self.convert(node.position, from: node)
let nodeFrameConverted = CGRect(origin: CGPoint(x:nodePositionConverted.x-node.frame.maxX,y:nodePositionConverted.y-node.frame.maxY),size:node.frame.size)
if nodeFrameConverted.contains(positionInScene), let n = node.name {print(n)}
}
}
}
Output:
This output showed differents taps. The first tap involved 3 nodes, the red 8 , the green 7 and the blue 6. Indeed you will have to your debug console:
The second tap involved only the green 7 and the blue 6:
The third and the fourth taps involved only blue and yellow nodes so you have respectively only the number of each node touched.
The last tap involved the red 8 and the blue 6 so you have:
Hope this help to your touches handle.
Upvotes: 3