如果我想知道哪个或哪些节点覆盖了场景的某个点,我可以scene.nodeAtPoint(point)
或scene.nodesAtPoint(point)
。有没有一种有效的方法可以用圈做类似的事情?这是为了能够找到位置与给定点相距一定距离的所有节点。
我需要这个能够更好地处理用户点击。场景相当稀少(没有太多用户活跃的精灵),所以没有必要要求用户非常精确地点击。我想在水龙头的坐标周围使用某种公差,并检测距离足够接近位置的节点。
我到目前为止最好的想法是多次nodeAtPoint()
:在水龙头的位置,然后在它的各种偏移处(例如,向上,向右,严格,...,左上方)。
答案 0 :(得分:5)
确定场景中的一个或多个节点是否在触摸事件的固定距离内,1)计算触摸与每个节点之间的距离,以及2)比较该距离是否小于或等于常数。以下是如何执行此操作的示例:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
let nodes = nodesNearPoint(self, point:location, maxDistance: 30)
if nodes.count > 0 {
print("node is near touch point")
}
}
}
// Returns an array of all SKNode objects in the subtree that near the specified point.
// If no nodes are near the point, an empty array is returned.
func nodesNearPoint(container:SKNode, point:CGPoint, maxDistance:CGFloat) -> [SKNode] {
var array = [SKNode]()
for node in container.children {
// Only test sprite nodes (optional)
if node is SKSpriteNode {
let dx = point.x - node.position.x
let dy = point.y - node.position.y
let distance = sqrt(dx*dx + dy*dy)
if (distance <= maxDistance) {
array.append(node)
}
}
}
return array
}
答案 1 :(得分:5)
首先,我们需要一个扩展来查找2 CGPoint(s)
extension CGPoint {
func distance(point: CGPoint) -> CGFloat {
return CGFloat(hypotf(Float(point.x - self.x), Float(point.y - self.y)))
}
}
现在,我们可以向SKScene
添加一个扩展程序,以查找距离给定点最近且位于maxDistance
内的子项。
extension SKScene {
func closestChild(point: CGPoint, maxDistance: CGFloat) -> SKNode? {
return self
.children
.filter { $0.position.distance(point) <= maxDistance }
.minElement { $0.0.position.distance(point) < $0.1.position.distance(point) }
}
}
上述扩展程序的计算复杂度为O(n)
,其中n
是场景的直接子项数。
答案 2 :(得分:2)
感谢所有的回答者,这就是我最终想出来的:
extension SKNode {
private func squareDistanceTo(point: CGPoint) -> CGFloat {
let dx = point.x - position.x
let dy = point.y - position.y
return (dx * dx + dy * dy)
}
func nearestNode(point: CGPoint) -> SKNode {
return children.minElement() {
$0.squareDistanceTo(point) < $1.squareDistanceTo(point)
} ?? self
}
func nodesInVicinity(point: CGPoint, tolerance: CGFloat) -> [SKNode] {
let squareTolerance = tolerance * tolerance
return children.filter() {
$0.squareDistanceTo(point) <= squareTolerance
}
}
}
上面扩展了SKNode
功能:
nearestNode
查找最接近特定点的子节点,或者如果没有子节点则返回父节点。nodesInVicinity
获取位置距离给定点一定距离的节点数组。经过一些测试,我得出的结论是,即使nodeAtPoint
具有O(n)复杂性,也没有希望获得快速获胜。
答案 3 :(得分:0)
我之前处理过的一种方法是创建一个圆(使用圆圈的png),将大小设置为我感兴趣的半径,然后查看该半径中的节点。
这是基本的想法,假设otherNodes
是我有兴趣检查的节点数组:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let radius = 500
let circle = SKSpriteNode(texture: SKTexture(imageNamed: "circle.png"), color: UIColor.clearColor(), size: CGSize(width: radius, height: radius))
circle.position = touches.first!.locationInNode(self)
for node in otherNodes {
if circle.containsPoint(node.position) {
//The center of this node falls in the radius
}
}
}
编辑:
在查看我使用过的旧解决方案之后,我注意到containsPoint
实际上是在查看该点是否在节点的bounding box
内,因此有一个圆的png实际上是不相关的,因此,匹配可能落在圆的半径之外,但仍在其边界框内。