首先声明一下,我完全了解SpriteKit的物理体可以检测碰撞,但这并不是我想要做的。我正在尝试构建一个潜在场AI系统,而计算吸引和排斥向量的一部分需要知道两个对象之间的距离。
我以为计算距离只需使用距离公式就行了,但不知为何我的距离计算结果非常不准确,所以我想咨询一下是否有人能发现我做错了什么。
下面我使用了一个简单的距离公式,点的位置对齐到它们的中心。
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}
这是我用来创建吸引向量的函数。
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)}
我使用类似的数学方法来计算排斥向量:
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)}
不幸的是,距离公式显然对我不起作用,我多次发现节点在接触时没有被检测到,或者在未接触时被误认为是接触的。是否有人能看出我哪里做错了,或者建议一个更简单的方法来准确计算两个SpriteNodes之间的距离?希望问题很简单。谢谢。
下图显示了我的AINode解决迷宫(橙色),一个需要避开的障碍物(蓝色)和我的目标(绿色)。
在这个例子中,AINode的宽度为20。障碍物的宽度为50。
在调试器暂停在AINode非法穿越障碍物的无效状态下,使用上述公式计算中心点之间的距离读数为“28.28”。实际距离是7。
更新
我发现我的吸引和排斥向量存在一个重大错误,这加剧了问题。我的距离公式有点偏差,但由于我计算角度的方式错误而被放大了。角度需要使用atan2
而不是atan
来计算。原因是如果不使用atan2
,返回的角度将位于错误的象限,这扭曲了排斥向量的方向并导致了重大错误。
回答: