【发布时间】:2016-03-17 01:08:39
【问题描述】:
我有一个(移动的)精灵节点。
我想在这个节点的一定范围内检测其他(移动的)精灵节点。一旦检测到一个,它应该执行一个动作。
玩动作部分对我来说没问题,但我似乎无法弄清楚范围内的检测。有什么想法如何去做吗?
【问题讨论】:
标签: swift sprite-kit
我有一个(移动的)精灵节点。
我想在这个节点的一定范围内检测其他(移动的)精灵节点。一旦检测到一个,它应该执行一个动作。
玩动作部分对我来说没问题,但我似乎无法弄清楚范围内的检测。有什么想法如何去做吗?
【问题讨论】:
标签: swift sprite-kit
一个简单但有效的方法是在场景的didEvaluateActions 方法中比较位置。 didEvaluateActions 每帧调用一次(在评估动作之后但在运行物理模拟计算之前)。您触发的任何新操作都将在下一帧开始评估。
由于计算真实距离需要平方根运算(这可能代价高昂),我们可以编写自己的squaredDistance 并跳过该步骤。只要我们的检测范围/半径也是平方的,我们的比较就会按预期进行。此示例显示“真实范围”为 25 的检测。
// calculated the squared distance to avoid costly sqrt operation
func squaredDistance(p1: CGPoint, p2: CGPoint) -> CGFloat {
return pow(p2.x - p1.x, 2) + pow(p2.x - p1.x, 2)
}
// override the didEvaluateActions function of your scene
public override func didEvaluateActions() {
// assumes main node is called nodeToTest and
// all the nodes to check are in the array nodesToDetect
let squaredRadius: CGFloat = 25 * 25
for node in nodesToDetect {
if squareDistance(nodeToTest.position, p2: node.position) < squaredRadius {
// trigger action
}
}
}
如果该操作应该只触发一次,则您需要在第一次检测后跳出循环并添加某种检查,以便在没有适当冷却时间的情况下在下一次更新时不会再次触发它。您可能还需要将位置转换为正确的坐标系。
另外,看看documentation for SKScene。根据您的设置,didEvaluateActions 可能不是您的最佳选择。例如,如果您的游戏还依赖物理来移动节点,最好将此逻辑移动到didFinishUpdate(场景渲染前的最终回调,在所有动作、物理模拟和约束应用于帧后调用) .
【讨论】:
在不影响性能的情况下,我能想到的最简单的方法是添加一个带有SKPhysicsBody 的子SKNode 用于您要命中的范围,并使用这个新节点contactBitMask 来确定它们是否在该范围内。
类似这样的东西(伪代码):
//Somewhere inside of setup of node
let node = SKNode()
node.physicsBody = SKPhysicsBody(circleOfRadius: 100)
node.categoryBitMask = DistanceCategory
node.contactBitMask = EnemyCategory
sprite.addNode(node)
//GameScene
func didBeginContact(...)
{
if body1 contactacted body2
{
do something with body1.node.parent
//we want parent because the contact is going to test the bigger node
}
}
【讨论】: