如何获取两个 SKSpriteNode 对象之间的距离以用于势场算法?

Uno*_*ome 1 artificial-intelligence ios swift

首先作为免责声明,我完全知道 SpriteKit 物理体能够收集碰撞。这不是我想做的。我正在尝试构建一个势场人工智能,并进行吸引力和排斥向量的部分计算,需要两个物体之间的距离。

我认为找到距离只需使用距离公式,但由于某种原因,我得到的距离结果非常不稳定,所以我想咨询是否有人能发现我做错了什么。

下面我使用一个简单的距离公式,其中点位置与其中心对齐。

func getDistanceBetweenTwoObjects(sourceNode:SKSpriteNode, destinationNode:SKSpriteNode) -> CGFloat{
    var centerSourceNode = CGPoint(x: sourceNode.position.x, y: sourceNode.position.y)
    centerSourceNode.x += sourceNode.size.width/2
    centerSourceNode.y += sourceNode.size.width/2

    var centerDestNode = CGPoint(x: destinationNode.position.x, y: destinationNode.position.y)
    centerDestNode.x += destinationNode.size.width/2
    centerDestNode.y += destinationNode.size.width/2
    var distance = sqrt(pow((centerSourceNode.x - centerDestNode.x), 2) + pow((centerSourceNode.y - centerDestNode.y), 2))
    return distance
}
Run Code Online (Sandbox Code Playgroud)

这是我用来创建吸引力向量的函数。

func getAttractionVector(node:SKSpriteNode, destinationNode:SKSpriteNode, spread:CGFloat, strength:CGFloat) -> (xSpeed: CGFloat, ySpeed: CGFloat){
    var sourceNodeRadius = sqrt(pow(node.size.width/2,2))
    var destNodeRadius = sqrt(pow(destinationNode.size.width/2,2))
    var radius = sourceNodeRadius + destNodeRadius
    var distance = getDistanceBetweenTwoObjects(node, destinationNode: destinationNode)
    if distance < radius{
        return (0,0)
    }
    var angle = atan((destinationNode.position.y - node.position.y)/(destinationNode.position.x - node.position.x))
    var xSpeed:CGFloat = 0
    var ySpeed:CGFloat = 0

    if radius <= distance && distance <= spread + radius{
        xSpeed = strength * (distance - radius) * cos(angle)
        ySpeed = strength * (distance - radius) * sin(angle)
    }else if distance > spread + radius{
        xSpeed = strength * spread * cos(angle)
        ySpeed = strength * spread * sin(angle)
    }
    return (xSpeed, ySpeed)
}
Run Code Online (Sandbox Code Playgroud)

我使用类似的数学来计算排斥向量:

    func getRepelVector(node:SKSpriteNode, destinationNode:SKSpriteNode, spread:CGFloat, strength:CGFloat) -> (xSpeed: CGFloat, ySpeed: CGFloat){

    var sourceNodeRadius = sqrt(pow(node.size.width/2,2))
    var destNodeRadius = sqrt(pow(destinationNode.size.width/2,2))
    var radius = sourceNodeRadius + destNodeRadius
    var distance = getDistanceBetweenTwoObjects(node, destinationNode: destinationNode)
    if distance > spread + radius{
        return (0,0)
    }

    var angle = atan((destinationNode.position.y - node.position.y)/(destinationNode.position.x - node.position.x))
    var xSpeed:CGFloat = 0
    var ySpeed:CGFloat = 0
    println("\(distance) : \(radius)")
    if radius <= distance && distance <= spread + radius{
        xSpeed = strength * (spread + radius - distance) * cos(angle)
        ySpeed = strength * (spread + radius - distance) * sin(angle)
    }else if distance < radius{
        xSpeed = getSign(cos(angle)) * CGFloat.max
        ySpeed = getSign(sin(angle)) * CGFloat.max
        println("HERE")
    }
    return (xSpeed, ySpeed)
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,距离公式显然让我失望了,我多次捕捉到节点,要么接触但它没有拿起它,或者不接触,但认为它接触。谁能看出我在哪里做错了,或者建议一种更简单的方法来准确计算两个 SpriteNode 之间的距离?希望它是简单的东西。谢谢。

下图显示了我的 AINode 正在解决我的迷宫(橙色)、需要避开的障碍(蓝色)和我的目标(绿色)。

在此示例中,AINode 的宽度为 20。在此示例中,障碍物的宽度为 50。

当调试器暂停在 AINode 非法与障碍物相交的无效状态时,使用上面提供的公式得出的中心点之间的距离读数为“28.28”。实际距离是7。 在此输入图像描述

更新

我在吸引和排斥向量中发现了一个重大错误,这加剧了问题。我的距离公式有点偏差,但由于我计算角度的错误,它被放大了。角度需要用atan2not来计算atan。原因是,如果atan2不使用该角度,则会在不正确的象限中返回角度,这会扭曲排斥矢量的方向并导致重大错误。

T. *_*sen 5

Larry 可能有所发现,因为您的使用centerSourceNode.x += sourceNode.size.width/2表明您的节点的锚点为 (0,0) 而不是默认的 (0.5, 0.5)。

使用默认值时,精灵的位置是其中心在父节点中的位置,这意味着除了两个节点的位置之外,您不需要考虑任何内容。

正如我在评论中提到的,这也使我们能够通过 CGPoint 的扩展来解决它:

extension CGPoint {
    func distanceFromCGPoint(point:CGPoint)->CGFloat{
        return sqrt(pow(self.x - point.x,2) + pow(self.y - point.y,2))
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,无论您在哪里需要这个,您都可以通过简单的调用来获取距离:

let distance = spriteA.position.distanceFromCGPoint(spriteB.position)