找到距给定点一定距离内的所有SKNode的有效方法?

时间:2015-12-26 18:54:14

标签: swift sprite-kit

如果我想知道哪个或哪些节点覆盖了场景的某个点,我可以scene.nodeAtPoint(point)scene.nodesAtPoint(point)。有没有一种有效的方法可以用做类似的事情?这是为了能够找到位置与给定点相距一定距离的所有节点。

我需要这个能够更好地处理用户点击。场景相当稀少(没有太多用户活跃的精灵),所以没有必要要求用户非常精确地点击。我想在水龙头的坐标周围使用某种公差,并检测距离足够接近位置的节点。

我到目前为止最好的想法是多次nodeAtPoint():在水龙头的位置,然后在它的各种偏移处(例如,向上,向右,严格,...,左上方)。

4 个答案:

答案 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实际上是不相关的,因此,匹配可能落在圆的半径之外,但仍在其边界框内。